.LEFT MARGIN 4.RIGHT MARGIN 72 .TITLE Converting Quad Word Dates to/from ASCII on PDP-11s .SUBTITLE B.#Z.#Lederman .FIGURE 3 .CENTER Converting Quad Word Dates to/from ASCII on PDP-11s .BLANK .CENTER B. Z. Lederman .BLANK .CENTER 2572 E.#22nd St. .CENTER Brooklyn, N.Y. 11235 .PARAGRAPH The quad word date format is being used more often on many systems: it is used as the DATE type in Datatrieve (all versions), and as the system date on VMS (and I believe TOPS also uses dates in this format). Even some PDP-11 only applications, such as RSX-11M-Plus System Accounting, outputs dates and times in this format. While they are easily read by Datatrieve, there are times when the data must be processed in another language (sad, but true). For example, you might want to maintain, query, and report a large amount of data with Datatrieve, but want to do number crunching on it with Fortran for some functions which are not easily implemented in Datatrieve like Fourier Analysis, Analysis of Variance, Matrix inversion, or whatever. Most PDP-11 languages don't have any provision for dealing with quad word integers, particularly dates expressed as clunks, and especially when the order of most significant to least significant word goes in the opposite direction from normal PDP-11 integers. (VMS people can call some SYS$ functions which will do various conversions: I believe examples of using these functions in Fortran have been published recently in a newsletter, and VMS documentation shows their use in Macro.) .PARAGRAPH If you want to convert a date in ASCII characters to a quad word value in clunks, there are several methods. The first was published in the ^&Wombat Examiner\&, Volume# _#3, Number#_#1, February 1981, Page 29. Anne Duncan of DEC told how to use an internal routine from Datatrieve to perform the conversion (this is not callable Datatrieve: you extract an object module from the distribution kit and use it in your program). This is really useable for Macro programmers only. .PARAGRAPH I recently had to go the other way: convert a quad word to an ASCII string. There is undoubtedly another internal Datatrieve-11 routine that does this, but it would be difficult to find. The Colorado Telephone Support Center happened to have someone on loan the day I called who knew that such a routine had once been supplied by DEC. .PARAGRAPH The routine to convert a quad word binary time to an ASCII character string is called $CDTTA, and it used to be in Appendix C of the ^&RMS-11 Macro-11 Reference Manual\& (AA-H683A-TC) supplied with RMS-11 Version 1.8; but it is not in the manual for version 2.0, the current version of RMS (the version you should have to run the current version of DTR-11 regardless of which operating system you have). I don't know why it's not documented anymore, but it is still supplied. If you still have your old manuals (see, there is a reason for keeping all of that old stuff heaped up in your office), you can look up the routine there. (The source listing in the old manual is slightly out of date in that some of the other routines called have been replaced by standard System Library routines, but the basic mode of operation is unchanged.) Knowing that there was one undocumented conversion routine aroused my curiosity, and when I started looking around I found a routine which goes the other way, converting an ASCII character date (and time) to a quad word integer in clunks: this is easier to use than the internal Datatrieve module mentioned before. For those of you who don't have the old manual, and for the routines that were never documented, I will give a description of how to use the routines from several languages (Macro, Fortran, Basic, etc.). .PARAGRAPH If you are writing programs in Macro-11, you can call the routines directly: the procedure is just like other system library formatting routines such as $CBTMG, $CBTA, etc. Addresses of buffers are loaded into registers, and the routine is called. I have placed a summary of the routines at the end of the article in the same format as the summary HELP messages obtained from RSX for the other library routines. The ^&IAS/RSX-11 System Library Routines Reference Manual\& tells how the documented routines are used. .PARAGRAPH If you are programming in Fortran (either Fortran-77 or the older Fortran-IV-Plus), then you cannot call these system routines directly. To solve this, I wrote my own subroutine which makes the $CDTTA and $CATDT routines available to Fortran programs. I am including the entire subroutine later in this article: the actual routine is so simple that it is shorter than the documentation, and most of that is argument error checking (If you were willing to assume that the people using the routine would always pass exactly the right arguments, the routines would only be three or four instructions long, but I think it's better to check anyway, just in case). The CDTTA routine has an optional flag so you can choose if you want the date, time or both, and times with and without seconds: this flag is set as shown in the Macro routine documentation for R2, also at the end of the article. If you leave out the flag, the routine will convert the date portion only, and it is a limitation of the system routine that it always converts the year as four digits (i.e., 1985 rather than just 85) so you should always allocate enough space for it. You don't need to know Macro yourself to use this routine. Assuming you typed in the macro source with the file name QUADAS.MAC, simply assemble it with the command: .BLANK.INDENT 5 MAC#QUADAS#=#QUADAS .BLANK in MCR or .BLANK.INDENT 5 MACRO/OBJ#QUADAS .BLANK in DCL and include QUADAS.OBJ when you task build (and the other modules listed below under task building). I haven't passed back the conversion error indicator, but you can easily test for errors yourself. Before conversion, zero or blank out the output area. If after calling the conversion routine the output area is still zero or blank, the conversion failed. .PARAGRAPH The most common cause of error is a syntactically incorrect date (such as asking for 30-FEB-85 to be converted to binary). When calling CATDT, the date must be in the format: .BLANK.INDENT 5 HH:MM:SS#MM/DD/YY##or##HH:MM#MM/DD/YY .BLANK or .BLANK.INDENT 5 HH:MM:SS#DD-MMM-YYYY##or##HH:MM#DD-MMM-YYYY .BLANK and the routine will test to see which version of date is used by looking for "/" and "-" characters and will check to see if the time includes seconds (it can be HH:MM only). Also, the year here can be two digits (assumed to be between 1900 and 1999) or four digits. IMPORTANT: if you use the 20-JUL-85 date format the month MUST be in UPPER CASE. The conversion routine does not convert lower case to upper case (though it is my opinion that it should). If you are interested in dates only, just put in 00:00 as the time and it will work from midnight as Datatrieve does. In calling the CDTTA routine, almost any positive number stored as a quad word integer should translate to a date without error. I have included a sample program calling these routines from Fortran in this article. I don't know if the old Fortran-4 will work, as it may use a different subroutine calling convention. .PARAGRAPH If you are using BASIC-Plus-2 or an equivalent DEC BASIC-Plus (which compiles programs), then you can also use this Fortran callable routine. You will have to use the .BLANK CALL#xxx#BY#REF#(arg, arg) .BLANK format to pass the values properly: the BASIC manuals should tell you how to do this. If you are using the old BASIC-11 (interpreter only) you can still use the routines, but they will have to be linked into the BASIC task. The person who installs BASIC on your system should check the manuals on how this is done, and will need to know a little about assembling and task building. .PARAGRAPH I have limited experience with other DEC languages, but a check of some manuals indicates that they also use the Fortran calling convention: if they can call system services such as ASNLUN, CLREF, WTQIO, etc., then they can call these routines. COBOL-81 should work with the construction .BLANK CALL "xxx" USING arg arg .BLANK and DIBOL should work with an XCALL. PASCAL can also call routines if they are defined as external. In all cases you should consult the appropriate language documentation. .PARAGRAPH Some DECUS languages such as "C" and some of the Pascals should also be able to do this, or could be modified to call the system routine directly, though I haven't yet looked into this myself. "C" sometimes has the facility to place values and addresses into known registers, which would allow you to call the routines in the same manner as Macro would, otherwise the Fortran calling convention should be used. Every vendor's Pascal works differently, and most make up their own subroutine convention, though DEC's R5 Fortran convention is a good one and allows you to call all sorts of system and DECNet services, and graphics on the PRO. .PARAGRAPH Task Building: you must include some system modules when you build (or LINK). On the current version of RSX-11M-Plus and P/OS (for the PRO-350) all of the modules are already in the system library, and Bill Tabor of the OA SIG informs me that all of the modules except $DIVD are in the system library on RSTS/E V8 (we are looking for another source for $DIVD: worst case is to type in the code as listed in the V1.8 RMS Macro manual mentioned earlier in the article). There is one catch connected with $DIVD you must watch out for. Normally, the Task Builder searches the system library for any undefined references: if you reference $CDTTA, the other modules used by $CDTTA should be included automatically. Unfortunately, on RSX-11M-Plus the module $DIVD has no entry point (as currently entered in the RSX system library): this means there must be an explicit reference to it in your task build command or ODL file, and I have an example of this later in the article. Alternativley, you can use the LBR utility to extract and then re-insert the $DIVD module, which will then give it a normal entry point so that no special reference will be needed when you build. On P/OS, the module already has an entry point, so you don't have to do anything special to use it. On other systems, if the modules are not in the system library they could be obtained from RMSUTL.OLB, which is in [1,24] on RSX distributions but may be elsewhere on other systems. As this library may not have been moved from the distribution kit to your actual system pack, you might have to ask the system manager to put it someplace where you can read it when you task build (LINK), or the system manager can move the modules to the system library so you will obtain them by default. Again, $DIVD might not be included and you may have to type it in from the source or perhaps pick it up from a previous release of your operating system or RMS. To use CDTTA you will need $CDTTA, $DIVD, $DIVQ, and $MONTB; to use CATDT you will need $CATDT, $MULQ and $GDTIM: to use the Fortran interface in this article, you need all of these modules as both CDTTA and CATDT are referenced. Some other system library routines will be called such as CATB, SAVAL, and ARITH, but these should already be in your system library as they they are documented and supported. Since the System Library Manual states that it is current for RSX-11M V4.1, IAS V3.1, RSX-11D V6.2 and RSX-11M-Plus V2.1, I would expect these system libraries (and Micro-RSX) to have the necessary modules, but I have no way to check them all. (It's quite possible that RSX-11D won't have them, but I can't think of any reason why they would not work if moved over from another source. Licencing may be a problem, though.)# If the system manager puts all of the necessary modules into the system library, then you should automatically get the ones you need when you task build without having to specify them in your task build command or ODL file unless you want to. .PARAGRAPH You should keep in mind that these are undocumented routines, so that DEC won't necessarily support them or answer questions about them: however, they are working and are simple enough that they shouldn't suddenly fail. Since one routine used to be documented and the others are in the library, a little polite pressure from the user community should be enough to convice DEC that the routines are worth documenting again. DECUS supplies this information strictly on an "AS IS" basis with no warranty, though I would be interested in hearing from people who use the routines. .TITLE Subroutine to make Quadword/ASCII conversion callable from Fortran .PAGE.NO JUSTIFY.NO FILL ########.TITLE QUADAS .IDENT /V1.0/ ; ; B. Z. Lederman 22-Jun-85 ; ; This Fortran callable subroutine makes the $CDTTA and $CATDT ; routines which convert between Quadword Binary date/time ; values in clunks (such as values from VMS, Datatrieve, and ; System Accounting) and ASCII strings available to Fortran (77 ; and IV-Plus) programs and other lanquages using the Fortran ; calling convention. ; ; These routines use R0, R1, R2, and R4. They don't save or ; restore any registers because the Fortran compiler assumes ; they will all be distroyed and saves them when necessary. ; Also, the conversion routines themselves save registers. ; ; Errors are handled in the simplest manner. If the routine ; is called with the wrong number of arguments, it simply does ; nothing and returns. If the conversion fails, the system ; routine returns with carry set, but this isn't passed back. ; The easiest way to check for errors is to zero your output ; area before calling and check it after calling: if it's still ; zero, the conversion failed. The most likely cause is an ; incorrect date format to CATDT: nearly any positive integer ; passed to CDTTA will result in a date. Note that the routines ; have no way of checking to see that you have allocated ; sufficent space for the output data, so if you haven't, one ; of your other variables will be clobbered. ; ; The necessary routines should be in SYSLIB or in ; [1,24]RMSUTL.OLB, except that $DIVD must be called ; explicitly as it has no entry point. ; ; ; CDTTA: Convert Quad Word to ASCII (this routine does the date ; portion only). ; ; CALL CDTTA( IN, OUT, [FLAG]) ; ; IN is a four word Integer*2 array (or equivalent) which holds ; the 64 bit binary time. ; ; OUT is a byte array (at least 11 bytes long) to hold the ; converted date. It must be this long because the year is ; four digits (as in 12-JUN-1985). ; ; FLAG is an optional argument to specify the items converted ; (see documents on $CDTTA): if absent, only the date (no ; time) is converted. ; .BLANK .PSECT $CODE2, RO, I .BLANK.TEST PAGE 24 CDTTA:: MOV (R5)+, R4 ; get number of arguments passed BEQ 50$ ; if no arguments, quit CMP R4, _#2 ; are there at least two arguments? BLT 50$ ; quit if no MOV (R5)+, R1 ; retrieve address of input data CMP _#-1, R1 ; was this a dummy argument? BEQ 50$ ; no dummy arguments allowed MOV (R5)+, R0 ; retrieve address of output area CMP _#-1, R0 ; was this a dummy? BEQ 50$ ; quit if yes CMP R4, _#2 ; were there only two arguments BEQ 20$ ; branch if yes (third is optional) CMP R4, _#3 ; double check for 3 arguments BNE 50$ ; not valid unless 2 or 3 arguments MOV (R5), R4 ; get address of flags CMP _#-1, R4 ; is it a dummy? BEQ 20$ ; yes, fill in our own flags MOV (R4), R2 ; use the callers flags BR 30$ ; skip our flags 20$: MOV _#2, R2 ; flag conversion of date only 30$: CALL $CDTTA ; call system routine 50$: RTS PC ; all done .BLANK.TEST PAGE 21 ; CATDT: Convert ASCII TIME and Date to Quad Word: ; (Must have a time, even if it's 00:00) ; ; CALL CATDT( IN, OUT) ; ; IN is a byte array which holds the time and date to ; be converted ; ; OUT is a four word Integer*2 array (or equivalent) ; which holds the 64 bit binary time. ; CATDT:: CMP (R5)+, _#2 ; were there two arguments? BNE 150$ ; quit if not exactly two arguments MOV (R5)+, R0 ; retrieve address of input data CMP _#-1, R0 ; was it a dummy argument? BEQ 150$ ; quit if dummy MOV (R5), R1 ; retrieve address of output area CMP _#-1, R1 ; was this a dummy? BEQ 150$ ; quit if dummy CALL $CATDT ; call system routine 150$: RTS PC ; all done .BLANK.TEST PAGE 18 ; GDTIM: Get the System Date/Time as a quad word ; ; CALL GDTIM( OUT) ; ; OUT is a four word Integer*2 array (or equivalent) ; which holds the 64 bit binary time. ; GDTIM:: CMP (R5)+, _#1 ; was there exactly one argument? BNE 250$ ; quit if no MOV (R5), R1 ; retrieve address of output area CMP _#-1, R1 ; was this a dummy? BEQ 250$ ; quit if dummy address CALL $GDTIM ; call system routine 250$: RTS PC ; all done .BLANK .END .TITLE Sample program to show how conversion routines work. .PAGE PROGRAM TST C C show conversion from ASCII to quad word date to ASCII C INTEGER QUAD(4), FLAG BYTE DATEIN(20), DATOUT(20) C C Clear out the date strings. C 10 DO 20 I = 1, 20 DATEIN(I) = ' ' 20 DATOUT(I) = ' ' C C Prompt the user for a date C WRITE (5, 30) 30 FORMAT(' Enter a date string [HH:MM(:SS) MM/DD/YY or' / 1 ' HH:MM(:SS) DD-MMM-YYYY]' / 2 '$ or RETURN for the current system date: ') READ (5, 40, END = 999) I, DATEIN 40 FORMAT( Q, 20A1) C C Re-set the quad word date C QUAD(1) = 0 QUAD(2) = 0 QUAD(3) = 0 QUAD(4) = 0 C C Check the number of characters input C IF (I .EQ. 0) THEN C C Get the system date/time as a quad word C CALL GDTIM( QUAD) C ELSE C C Convert the ASCII input to a quad word C CALL CATDT( DATEIN, QUAD) C ENDIF C C Show the conversion C WRITE (5, 50) QUAD 50 FORMAT( 4(3X, I7)) C C Convert back the other way (default flags) C CALL CDTTA( QUAD, DATOUT) C WRITE (5, 60) DATOUT 60 FORMAT (1X, 20A1) C C Demonstrate different formats with conversion flags C DO 100 FLAG = 0, 15 C clear the output area DO 70 I = 1, 20 70 DATOUT(I) = ' ' C CALL CDTTA( QUAD, DATOUT, FLAG) C WRITE (5, 80) FLAG, DATOUT 80 FORMAT (1X, I3, 1X, 20A1) C 100 CONTINUE C GOTO 10 C 999 CALL EXIT C END .TITLE Task build file for the demonstration program .PAGE .BLANK TST, TST/MA/-WI = TST, QUADAS, ; include interface LB:[1,1]F77FCS/LB ; (Fortran OTS library, if not in SYSLIB) LB:[1,1]SYSLIB/LB:$DIVD ; must specify this module if $DIVD ; doesn't have an entry point / ; other modules come in by default ACTFIL=1 UNITS=5 SUPLIB=FCSFSL:SV ; optional M-Plus feature // .TITLE Descriptions of Macro interface to Library routines .LEFT MARGIN 0.RIGHT MARGIN 80 .PAGE .CENTER $CBTTA .BLANK Convert binary Quad word date/time to ASCII string Input: Output: R0 = address of output buffer R0 = points to next byte in output buffer after converted string R1 = address of quad word to be converted R2 = conversion flag: bit 0 = 0 -> convert date portion bit 0 = 1 -> do not convert date bit 1 = 0 -> convert time portion bit 1 = 1 -> do not convert time bit 2 = 0 -> time in 24 hour format bit 2 = 1 -> time in AM/PM format (might not be functioning) bit 3 = 0 -> convert time in seconds bit 3 = 1 -> do not include seconds in time Call: CALL $CDTTA .BLANK.TEST PAGE 14 .CENTER $CATDT .BLANK Convert ASCII string to binary Quad word date/time Input: Output: R0 = address of input string R0 = points to next byte in input buffer after converted string R1 = address of quad word to receive value R2 = not used Call: CALL $CATDT .BLANK.TEST PAGE 14 .CENTER $GDTIM .BLANK Retrieve system date/time as binary Quad word Input: Output: R0 = not used R1 = address of quad word to receive value Call: CALL $GDTIM .BLANK