Hi everybody. Here's an update of <-LIB-> to version 1.4 Features: - building/splitting libraries and unsupported objects - implemented as library, id no. 1221 - fast; build an 8.5 kb library in ~32s, split it in ~22s - compatible to USRLIB.EXE Changes vs. version 1.0: - change names: ->LIB to D->LIB, ->DIR to L->DIR, ->SAR to ->$AR, ->SYM to ->ALG, ->RRP to ->DIR, ADDR to ADR?, PRCL to PORT-> - add RHASH, RLINK, PGLIB, LIB? - make protection for selfextraction public - add reports to L->DIR - L->DIR didn't searches for ROMPTR in $CONFIG, fixed - an empty $VISIBLE list generated by L->DIR causes problems upon recalling, fixed - link tables may be empty, remove "No Objects to Include" message - ->DIR can't handle 0-ID's, add error generation - reduce memory usage of D->LIB Changes vs. version 1.2: - fix 0 ->ALG bug - add RMSG, RCFG - change the program beyond ->DIR - D->LIB produced strange libraries if the variables are not sorted visible 1st followed by the hidden ones, fixed. Changes vs. version 1.3: - chksum/bytes #FD74h/5036 - modify LIB? and L->DIR so they can handle 'holes' in the link table (real hidden commands). Copyright Notice: Copyright (c), 1992, Detlef Mueller, Raymond Hellstern, Rick Grevelle. Permission to copy this article is granted provided that the copies are not made or distributed for resale (excepting nominal copying fees). Other permissions can be arranged by contacting Detlef Mueller via email at the following addresses: detlef@mwhh.hanse.de <-LIB-> is distributed in the hope that it will be useful, but WITHOUT *ANY* WARRANTY. Installation: To install <-LIB->, a) download the ASC version, execute ASC->, or b) download the uudecoded version and recall it into the stack. Enter the port number (0,1,2) where you want to store it and press STO. Switch the HP48 off, then on again. <-LIB-> will now be installed. Deinstallation: To get rid of <-LIB-> execute the following commands (assuming that the port where <-LIB-> is stored have R/W acess) : HOME @ switch to the home directory 1221 DETACH @ release library :&:1221 PURGE @ search and purge library Programs (in order of appearance in the LIBRARY <-LIB-> menu): D->LIB ( --> lib ) D->LIB tries to assemble a library from the objects in the current directory. The creation process is controlled by a few variables with reserved names (see below). This control interface is adapted from USRLIB.EXE, so I recommend to read USRLIB.DOC (both files are provided by HP in the self-extracting archive TOOLS.EXE (MesS-DOS), availabe on several ftp sites). L->DIR ( %libno --> dir ) ( romptr --> obj ) If the argument is a library id.no. then L->DIR tries to assemble a directory containing all commands of this library as variables. The neccessary control variables (see below) are also generated. L->LIB can be used to recreate the library. If the argument is a romptr then L->DIR tries to recall the associated library command onto the stack. You cannot use L->DIR to split <-LIB->, this should prevent 'normal' users from a memory lost. If you are conform with the internals of your HP48, use 'ROMPTR@' to extract single routines from <-LIB->, but beware of starting the routines in RAM, most of them will crash your calc when running standalone ! MCFG ( --> ) MCFG stores a configuration program in a variable named '$CONFIG' in the current directory. This configuration program will attach the generated library to the home directory at warmstarts (ON-C etc.) '$ROMID' must exist in the current directory. ML->D ( --> prg ) ML->D generates a program with the following interface: ( {} --> lib data ) (The list can contain anything) The program checks argument count/type and will generate an error if something is wrong. '$ROMID' must exist in the current directory. MD->L ( --> prg ) MD->L generates a program with the following interface: ( lib data --> {} 0 ) if the library ID contained in the library data object is correct, ( lib data --> 1 ) if the library ID contained in the library data object is invalid. The program checks argument count/type and will generate an error if something is wrong. '$ROMID' must exist in the current directory. Store the programs generated by ML->D and MD->L into variables in your library source directory and use use them as an interface to generate/resolve data associated to your library. OB-> ( prg --> ob1 .. obn %n ) ( xlib --> %libno %objno ) ( arry --> ob1 .. obn {%n %m} ) ( alg --> ob1 .. obn %n ) ( dir --> ob1 id .. obn id %n ) ( lib data --> {} %libno ) On any other object OB-> dispatches to OBJ->. OB-> is an extension to the built-in OBJ->. Just try it. ->PRG ( ob1 .. obn %n --> prg ) ->XLIB ( %libno %objno --> romptr ) ( #libno #objno --> romptr ) ->$AR ( ob1 .. obn %n --> array of string ) ->ALG ( ob1 .. obn %n --> prg ) ->DIR ( ob1 id .. obn id %n --> dir ) ->LD ( {} %libno --> library data ) Functions to reverse OB->. ADR? ( ob --> ob #addr ) Get address of ob. PORT-> ( %portno --> ob1 .. obn ) PRCL recalls all objects from the given port (0/1/2) onto the stack, ignoring the R/W status of that port. $romid $title $config $vars $hidden $visible $message ( --> '$XXX' ) These programs just put a control variable name onto the stack. RHASH ( %libno --> hxs ) Recall hash table; get a pointer to the hash table of the library with lib ID %libno. RLINK ( %libno --> hxs ) Recall link table; get a pointer to the link table of the library with lib ID %libno. PGLIB ( %libno --> ) Detach the library with the lib ID %libno from the home directory and try to purge it. LIB? ( %libno --> $ ) Gives you a detailed addressmap of the library with the lib ID %libno. The string starts with the title (if exist), followed by the 1st and last address of the lib and the Library ID. The remainder of the map is a list with one line of information for every XLIB entry in the lib. Structure of a line: 1st last xn name typ ||| |||| || |||| +++- Type of the object ||| |||| || ++++------ Name of the object (if it's a visible cmd) ||| |||| ++----------- XLIB number of the object ||| ++++-------------- Last address of the object +++------------------- First address of the object The list is sorted by address. Try 1221 LIB? or 2 LIB? If you find unexpected 'holes' between two objects or objects embedded in other objects, the library was not generated using USRLIB.EXE or D->LIB. Control Variables: The library creation process is controlled by some variables with reserved names: $ROMID Contains a real or binary number representing the library ID that is to be given to the library. If you want to release your developed software to the world, make shure that your choosen library ID is not used by others. The ID number must be in the range 769 - 1791. $TITLE Contains a string to be used as the name of the library. The first 3 to 5 characters of the title are used for the LIBRARY menu label; the first 22 characters are displayed by REVIEW. If $TITLE is absent or contains a null string, no title is generated and the resulting library is not shown in the LIBRARY menu. $CONFIG Contains a program to be executed at configuration time. The configuration code can generally NOT be written in user-accessible commands. Simple programs such as \<< 123 ATTACH \>> are OK, but more complicated programs should take care to leave the stack unchanged, and be sure NOT TO ERROR. I recommend to use MCFG to generate a configuration program. $MESSAGE Contains a list of strings to be combined into a message table. The message numbers correspond to the list positions. In user-RPL you can generate errors with own messages in the following manner: #32001h DOERR |||++-- Message number (here 1) +++---- Library ID (here #320h = 800) In this example DOERR will generate an error, using the first message from the message table of a library with the id 800. Note: The message list structure is NOT compatible to USRLIB.EXE. $VISIBLE Contains a list of names of variables to be converted to user- accessible, named library commands. By default, all variables will be translated to named library commands. When the $VISIBLE list is present, only the names in this list are included in the library hash table. An empty list is OK, meaning that no hash table is generated. $HIDDEN Contains a list of names of variables that are to be converted to null-named objects in the library, and so hidden from the user. When the $HIDDEN list is present, those names listed are not entered in the library hash table. If both $VISIBLE and $HIDDEN are present, only $HIDDEN will be used. $VARS Contains a list of variables that should remain RAM-based - the stored objects are not included in the library, and no XLIB pointers are made to substitute their names. All other variables in the source directory are included in the library. Note: You should include all subdirectory names of the directory (if any) in the list. $ROMID must exist in the current directory, all other control variables are optional - you can generate libraries containing only a configuration program or a message table ! All control variables are internally handled by D->LIB to be RAM-based. Error Messages: Along with the 'normal' error messages like 'Insufficient Memory' you can get one of the following messages by some of the programs: "Missing $ROMID" '$ROMID' is not defined in the current directory. You MUST create a variable named '$ROMID' and store a library ID into it. "$ROMID Not Real/Binary" '$ROMID' doesn't contain a real or binary object. "$ROMID Out of Range" The value of '$ROMID' is not in range 769-1791. "$CONFIG Not a Program" '$CONFIG' doesn't contain a program. Because the stack must not change at system halts, '$CONFIG' must contain a program. "$HIDDEN Not a List" "$VISIBLE Not a List" "$VARS Not a List" "$MESSAGE Not a List" $XXX must contain a list. "Found ID Name>16 Chars" One of the variable names in the current directory is greater than sixteen characters in size. "Found 0-ID" ->DIR or D->LIB have found a null-ID. Things to Notice: The library stucture is 'flat', so don't try to include subdirectories in a library, it can end up in a 'memory lost'. Not all program objects that execute correctly from global variables are directly convertible into libraries. Here are some pitfalls: - Since a library cannot be modified, no library command may be the target of a STO or PUT operation. - XLIB names are not usable in all contexts in which global names are valid arguments. This can cause constructs that reference a named object to fail. For example, 'A' 5 GETI where A is a list will not work when A is converted to an XLIB name. Instead use A 5 GETI - XLIB names are not valid as formal variables in algebraics, or as the independent variable for plotting or solving. - \->STR applied to a global name that is converted to a 'hidden' library command (see $HIDDEN) returns a null string. D->LIB needs ~(1.2 * size_of_source_directory) bytes to be free to generate a library. Reassembling a splitted library may be dangerous if the original library wasn't generated using USRLIB.EXE or D->LIB. There is no guarantee that the result will work probably - even if no code changes are done. USRLIB.EXE generates a link table entry for the configuration program; if you split such a library with L->DIR, you'll get the configuration code twice, the 1st one stored in $CONFIG, the 2nd one stored in a variable of the generated directory. Purge the variable before using D->LIB. You also can use LIB? to see the second reference to the config code. I didn't find informations about library data objects, so I take a close look at the HP EQLIB card and adapt the methods to create/resolve library data objects. In the HP EQLIB card (and in <-LIB->), a library data object is some sort of composite, has the prologue DOEXT0, and a body which is a sequence of objects and object pointers, the last of which is an object pointer whose pointee is the primitive code object SEMI. The body also includes a length field (indicating the length of the body) and the number of the library which have generated the data: +-----------------------+ | -> DOEXT0 | Prologue Address +-----------------------+ | Length Field | Body Library | ------------ | Data | Library ID | Object | ------------ | | Object/ | | Object Pointer | | Sequence | | ------------ | | -> SEMI | +-----------------------+ Converting such an object to a list is very simple, just change DOEXT0 to DOLIST and the length field to DOBINT. But there is no guarantee, that existing libraries (which generates library data) are using this structure; using OB-> (or the program generated by MD->L) on such an object may cause a memory lost. I'll maintain this tool, so feel free to mail me any comments, error descriptions, ideas of improvement, suggestions et c. ;-) Listings (sys-RPL in HP terminology) of the generated programs (#libid is generated from the library ID stored in $ROMID): Program created by MCFG : :: #libid TOSRRP ; Program created by ML->D : ASSEMBLE >HCOMP EQU #052C6 ( { .. } ob --> { ob .. } ); near >TCOMP RPL :: CK1NOLASTWD CK&DISPATCH1 list :: #libid >HCOMP ( --> { #rid ... } ) DUP OSIZE #5- ( --> {} #sz ) CODE ( {} #sz --> LibDat ) * CPU A C D1 GOSBVL =POP# sz GOSBVL =SAVPTR C=DAT1 A &{} D1=C &{} LC(5) =DOEXT0 LD DAT1=C A D1=D1+ 5 &1st elem (#) DAT1=A A GOVLNG =GETPTRLOOP ENDCODE ; ; Program created by MD->L : :: CK1NOLASTWD CK&DISPATCH1 #AF ( *Libaray data* ) :: #libid SWAP TOTEMPOB ( --> #rid ld ) CODE * CPU A C D1 GOSBVL =SAVPTR A=DAT1 A &ld D1=A &ld LC(5) =DOLIST {}-prol DAT1=C A D1=D1+ 5 &len LC(5) =DOBINT #-prol DAT1=C A GOVLNG =GETPTRLOOP ENDCODE DUP CDRCOMP ( #rid {} --> #rid {} {}-1st ) SWAP CARCOMP ( --> #rid {}-1st #rid' ) ROT #<> casedrop %1 %0 ; ; Credits: 1) Rick Grevelle L->DIR and DIR-> (in OB->) are basing on RCLIB, ->DIR and DIR-> (in OUT->) from the HACKIT library. He send me a new version of ->DIR (complete ML), which is verry fast and uses less memory. Also thanks for many suggestions. 2) Joseph K. Horn Thanks for SORTLS, I use the kernel of it in LIB? to sort the listing by address. 3) Romain Desplats, Georg Hoppen Thanks for testing all the beta releases 4) Chris Spell Thanks for sorting this chaos ;-) 5) W. C. Wickes, HP Corvallis HP48/RPL tools/ASC stuff Happy hacking, 8-Detlef. P.S. Any questions ? Feel free to mail me.