Subject: v06i001: lib_dm - <-LIB-> version 1.5, Part01/02 Keywords: hp48 library tool building/splitting objects Date: 6 Jul 92 01:11:00 GMT BEGIN_DOC lib_dm.doc Hi all, Here's an update of <-LIB-> to version 1.5 Features: o implemented as library (no. 1221), containing *37* commands o building/splitting libraries and un/supported objects o D->LIB/L->DIR are compatible to the USRLIB.EXE directory format o fast; build an 8.5 kb library in ~32s, split it in ~22s o small in size (~7kb) o standalone interface to library data objects o powerfull library management tools o many programming tools o many status commands Changes vs. version 1.4: - cksum/bytes: #9CFEh/6962 - add config message - reordered nearly all commands (sorry) - expand $ROMID range to 0..2047 - fix bug in D->LIB regarding generation of commands which are legal in algebraics - L->DIR produced a strange $ROMID for IDs < 10, fixed - L->DIR generated to few variables if # visible cmds << # hidden cmds, fixed - rename ->$AR to ->ARR, improve it - rename LIB? to LIBp, modify it - rename ADR? to ADRp - improve PGLIB - add fast mode to L->DIR, D->LIB - add BACKUP support to OB-> - add ->BAK, LBCRC, RNLIB, CHLID, RLIB, STLIB, ACLIB, INSTp, LIBSp, fEVAL - reviewed the documentation Please excuse all the linguistic mistakes in this text. English is not my native language. 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 address: 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. Purge the variable which contains the orginal copy. Enter the port number (0,1,2) where you want to store <-LIB-> 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 or 1221 PGLIB Note: In the documentation below I'll often use the abbreviation 'LID'; meaning 'library id'. It's a number in the range 0..2047 and is part of any library, used to identify libraries while resolving commands, messages et c. Command Quick Reference (in order of appearance in the LIBRARY <-LIB-> menu): Page 1: D->LIB ( --> lib ) make library L->DIR ( % --> dir ) split library MCFG ( --> ) make config program ML->D ( --> prg ) make libdat-> handler MD->L ( --> prg ) make ->libdat handler OB-> ( ob --> ? ) split object Page 2: ->DIR ( meta --> dir ) make directory ->PRG ( meta --> prg ) make program ->XLIB ( % % | # # --> xlib ) make XLIB ->ARR ( meta --> arry ) make array ->ALG ( meta --> alg ) make algebraics ->LD ( {} % --> libdat ) make library data Page 3: ->BAK ( ob $ --> bak ) make backup ADRp ( ob --> ob # ) get address of object $romid ( --> id ) get id '$ROMID' $visible ( --> id ) get id '$VISIBLE' $title ( --> id ) get id '$TITLE' $config ( --> id ) get id '$CONFIG' Page 4: $vars ( --> id ) get id '$VARS' $hidden ( --> id ) get id '$HIDDEN' $message ( --> id ) get id '$MESSAGE' LBCRC ( lib | bak --> lib' | bak' ) recalculate CRC RNLIB ( lib $ --> lib' ) rename library CHLID ( lib % --> lib' ) change LID Page 5: RHASH ( % --> C# ) get hash table RLINK ( % --> C# ) get link table RCFG ( % --> prg ) get config code RMSG ( % --> arry ) get message table RPORT ( % --> ob ... ) recall port RLIB ( % --> lib % ... ) recall library(s) Page 6: PGLIB ( % | :%:% --> ) purge library STLIB ( lib % --> ) store library ACLIB ( :%:% --> ) activate library LIBp ( % --> $ ) get library layout INSTp ( --> {} ) get installed LIDs LIBSp ( --> {} ) get available LIDs Page 7: fEVAL ( ob --> ? ) fast EVAL Detailed Command description (in order of appearance in the LIBRARY <-LIB-> menu): D->LIB ( --> lib ) Dir to lib; assembles a library from the objects of the current directory. The creation process is controlled by a few variables with reserved names (see 'Control Variables' below). This control interface is adapted from USRLIB.EXE, so I recommend you to read USRLIB.DOC also. Both files are provided by HP in the self- extracting archive TOOLS.EXE (MesS-DOS), availabe on several ftp sites. If you set flag -13 before running D->LIB, it'll switch off the screen while working (for saving time and batteries). L->DIR ( %LID --> dir ) ( XLIB --> obj ) Lib to dir; if the argument is a LID then L->DIR assembles a directory containing all commands of this library as variables. The necessary control variables (see below) are also generated. L->LIB may be used to recreate the library. If the argument is a XLIB (a named or unnamed library command) then L->DIR recalls its object from the associated library onto the stack. If you set flag -13 before running L->DIR on a library, it'll switch off the screen while working (for saving time and batteries). Note: You cannot use L->DIR to split <-LIB->, this should prevent 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 ( --> ) Make config; stores a configuration program into a variable named '$CONFIG' to 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 ) Generates a program with the following interface: ( {} --> lib_data ) The list can contain anything. The program checks for argument count and type and may error with: #201 - To Few Arguments #202 - Bad Argument Type '$ROMID' must exist in the current directory. MD->L ( --> prg ) Generates a program with the following interface: ( lib_data --> {} ) The program checks for argument count, type and correct LID and may error with: #201 - To Few Arguments #202 - Bad Argument Type #203 - Bad Argument Value '$ROMID' must exist in the current directory. Note: Store the programs generated by ML->D and MD->L into variables in your source directory and use use them as an interface to generate/ resolve data associated to the resulting library (see 'An Example' below). OB-> ( prg --> ob1 .. obn %n ) ( xlib --> %LID %objno ) ( arry --> ob1 .. obn { %di .. %d1 } ) ( alg --> ob1 .. obn %n ) ( dir --> ob1 id .. obn id %n ) ( lib_data --> {} %LID ) ( backup --> ob $ ) ( any_other --> dispatch to OBJ-> ) OB-> is an extension to the built-in OBJ->. Just try it. ->DIR ( ob1 id1 .. obn idn %n --> dir ) ->PRG ( ob1 .. obn %n --> prg ) ->XLIB ( %LID %objno --> XLIB ) ( #LID #objno --> XLIB ) ->ARR ( ob1 .. obn %n --> arry ) ( ob1 .. obn { %di .. %d1 } --> arry ) ->ALG ( ob1 .. obn %n --> prg ) ->LD ( {} %LID --> lib_data ) ->BAK ( ob $ --> backup ) Functions to reverse OB->. About ->ARR: Generates arrays of any type and any dimension (eg. a four dimensional array of libraries :-). %di * .. * %d1 must be = n, ob1 .. obn must be of the same type. All possible parameter errors are trapped. ADRp ( ob --> ob #addr ) Get address of ob. Note: The data stack is a stack of pointer to objects. ADR? simply returns the value from the top element (about the 'p', see 'LIBSp' below). $romid $visible $title $config $vars $hidden $message ( --> '$XXX' ) These commands just put a control variable identifier onto the stack. LBCRC ( lib --> lib' ) ( backup --> backup' ) Recalculates the CRC of a library or backup, usefull if you have patched it (modifying the body of a library or backup invalidates the included CRC). RNLIB ( lib $ --> lib' ) Renames a library (changes title). CHLID ( lib % --> lib' ) Change LID; this program allows you to change the LID of a library if it's not splittable. Note 1: If you're using the Config Suppressor, LID 4, you may change it's LID to 14 without any problem, because Simone already uses LID 4 for the KEEPER library. Sorry, I didn't know that :-( Note 2: Mostly the LID is also hardcoded in the config code - this can generally not be changed by CHLID. You have to attach the library manually after a warmstart. Also a library contains its LID coded in a field above any visible command (the error handling system identifies the command that causes an error using this field (the sys-RPL commands 'CKn' copies this values to the appropriate location)). These fields are also not changed by CHLID (maybe I'll add it in the next update (if any)). If HP is listening: You should redefine the stack frame during config time in the successor of the HP48. Having the LID of the actual library being configed on the stack (as a %) would allow any config code using this value for autoattachment. This would make changing of LIDs easy. :-) RHASH ( %LID --> C# ) Recall hash table; get a pointer to the hash table of a library. RLINK ( %LID --> C# ) Recall link table; get a pointer to the link table of a library. RCFG ( %LID --> ob ) Recall config code; get a pointer to the config code of a library. RMSG ( %LID --> arry ) Recall message table; get a pointer to the message table of a library. Note: RHASH, RLINK, RCFG and RMSG don't error if the library associated to %LID didn't contain the requested item, they leave the stack unchanged in that case. RPORT ( %port --> ob1 .. obn ) Recalls pointers to all objects of a given port (0/1/2) onto the stack, ignoring the R/W status of that port. RLIB ( :%port:%LID --> lib ) ( %LID --> libn %portn ... ) Recall lib; the 1st case recalls a library from a given port, the 2nd case searches ports 0,1,2 for libraries with %LID, returning all found libraries and the ports where they resides. Note: This command actually returns pointer to libraries (like RPORT), if you recall a lib and try to purge it while it's on the stack, you'll get a 'Object in use' error. Execute NEWOB or store it into a variable first. PGLIB ( :%port:%LID --> ) ( %LID --> ) Purge lib; the 1st case works like :%port: %LID PURGE, in the 2nd case the ports are searched in order 0,1,2 for a library with %LID. The difference to PURGE: if the found library is attached to the home directory, it's detached before purging. Also if there is a inactive library with the same LID in any other port, it becomes active and is attached to the HOME directory. STLIB ( lib %port --> ) Store lib into port; there're a few differences to STO: - The library is installed full; a warmstart isn't neccessary and thus not initiated at the next power cycle. All warmstart volatile variables (stack, PICT) remains intact. - The library last stored is visible to the HP48 (in case of having a library with the same LID installed in another port). - If flag 1 is clear, the library is attached simply to the home directory. - If flag 1 is set, the config code of the library is executed under warmstart conditions. This is usefull for testing a config code. ACLIB ( :%port:%LID --> ) Activate library. Using STO you can install libraries with the same LID on different ports, but only one will be visible to the HP48 at the time. During warmstarts the ports are searched in order 2,1,0 (most cases), ei. the library residing in the port with the highest number will be active. ACLIB allows you to switch to any other library with the same LID at runtime, the effect is immediate. ACLIB 1st detaches the LID from HOME, sets the new priority and than a) attaches the LID to HOME again if flag 1 is clear, or b) runs the library config code if flag 1 is set (like STLIB). Note: A typical work cycle for testing an update of a library (LID x), which previous version is stored in port 1 or 2 (n) might be: - create the update, put it on the stack then 0 STLIB - x MENU, testing ..... - oops, command failed. But need it now: :n:x ACLIB VAR x MENU - use old command then back to update: :0:x ACLIB VAR x MENU - testing ..... - want to save update: :0:x RLIB NEWOB or 'xyz' STO - ditch away new version: :0:x PGLIB or old version: :n:x PGLIB - ... A similar cycle, but only port 0 available: - create the update, put it on the stack - get old version, purge it from port 0, change LID, store it back and store update: x RLIB DROP NEWOB x PGLIB 800 CHLID 0 STLIB 0 STLIB - x MENU .... - oops, ... 800 MENU .... - ditch away update, recall old version, purge old version, change old version to original LID and store it back: x PGLIB 800 RLIB DROP NEWOB x CHLID 0 STLIB - ... LIBp ( %LID --> $ ) Returns a detailed layout of a library. The map starts with the title (if exist), followed by the 1st and last address of the lib and the LID. The remainder lists the contens of the lib, one line of information for each XLIB entry. 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 relative address of the object +++-------------------- Offset to startaddr. of the object The list is sorted by address. Try 1221 LIBp or 2 LIBp. Note: If you find unexpected 'holes' between two XLIBS (> 10 nibbs) or XLIBS embedded in other XLIBS, the library wasn't generated using USRLIB.EXE or D->LIB. INSTp ( --> { %LIDn .. %LID1 } ) Returns a list of all libraries attached to the current directory, { } if none. Note: You can PURGE libraries even if they are attached to a subdirectory. INSTp can be used to find such zombies. LIBSp ( --> { %LID1 .. %LIDn } ) Returns a list of all libraries currently installed on your HP48. Note: The '?' prefix denotes normally returning of a flag in RPL, the JARGON file, v2.9.9, 01 APR 1992 states: 3. The `-P' convention: ------------------------ Turning a word into a question by appending the syllable `P'; from the LISP convention of appending the letter `P' to denote a predicate (a boolean-valued function). The question should expect a yes/no answer, though it needn't. (See {T} and {NIL}.) At dinnertime: Q: "Foodp?" A: "Yeah, I'm pretty hungry." or "T!" At any time: Q: "State-of-the-world-P?" A: (Straight) "I'm about to go home." A: (Humorous) "Yes, the world has a state." so I used 'p' for general information questions ;-) fEVAL ( obj --> ? ) Works like EVAL, but switches the display off 1st. Speeds up evaluation ~11%. In case of an error or the obj have fineshed execution, the display is switched on again. Not very usefull, if obj prompts for input... Control Variables: The library creation process is controlled by some variables with reserved names (multiple occurences of these names are ignored, but only the contens of the 1st one found in the source directory is used for the library creation process) : $ROMID Contains a real or binary number representing the LID that is to be given to the library. If you want to release your developed software to the world, make shure that your choosen LID is not used by others. The LID must be in the range 0..2047. $TITLE Contains a string to be used as the name of the library. The first few 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) +++---- LID (here #320h = 800) In this example DOERR will generate an error, using the first message from the message table of a library with the LID 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 add all subdirectory names of the source directory to 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 commands: "Missing $ROMID" '$ROMID' is not defined in the current directory. You MUST create a variable named '$ROMID' and store a LID into it. "$ROMID Not Real/Binary" '$ROMID' doesn't contain a real or binary object. "$ROMID Out of Range" a) The value of '$ROMID' is > 2047. b) A real > 2047 was passed to CHLID. Note: Negative reals are mapped to 0. "$CONFIG Not a Program" Because the stack must not change during warmstarts, '$CONFIG' must contain a program. Note: Use 1 ->PRG to embedd CODE, IDs or XLIBs into 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" D->LIB have found a visible command identifier which is > 16 chars in size. "Found 0-ID" D->LIB have found a visible command identifier with the size 0. Things to Notice: +---------------------------------------------------+ | HP 48 Resource Allocation Guideline: Library ID's | +---------------------------------------------------+ | 0000 - 0256 Take-over libraries; do not use! | | 0257 - 0512 HP ROM-based libraries; do not use! | | 0513 - 0768 HP RAM-based libraries; do not use! | | 0769 - 1536 3rd Party (assigned by HP) | | 1537 - 1791 3rd Party (assigned by HP) | | 1792 - 2047 Command-line; do not use! | +---------------------------------------------------+ 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. If any visible command starts with the seqence '\<< \->' or '\->' it's marked in the library as a valid command for algebraics. If you press its associated softkey in ALG entry mode, you'll get 'name()'. Multiple occurences of variable names results in an incorrect library because only the contens of the 1st one is picked up. D->LIB needs ~(1.2 * size_of_source_directory) bytes to be free to generate a library. The time D->LIB needs for doing a job depends mainly on the total number of commands included in the resulting library. Eg. Raymond runs D->LIB on a ~60kb directory containing ~300 variables; D->LIB needs ~45min on a rev A HP48 to build the library (not in FAST-mode). 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 (thanks to Raymond for borrowing me one) 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 LID of the library which have generated the data: +-----------------------+ | -> DOEXT0 | Prologue Address +-----------------------+ | Length Field | Body Library | ------------ | Data | LID | Object | ------------ | | Object/ | | Object Pointer | | Sequence | | ------------ | | -> SEMI | +-----------------------+ Note: 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, questions, information, * * suggestions et c. :-) * ***************************************************************************** Listings (sys-RPL in HP terminology) of the generated programs (#LID is generated using the LID value stored in $ROMID): Program created by MCFG : :: #LID TOSRRP ; Program created by ML->D : ASSEMBLE >HCOMP EQU #052C6 ( { .. } ob --> { ob .. } ); near >TCOMP RPL :: CK1NOLASTWD CK&DISPATCH1 list :: #LID >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* ) :: #LID 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 #<>case SETSIZEERR ; ; Credits: 1) Rick Grevelle L->DIR and DIR-> (in OB->) are basing on RCLIB, ->DIR and DIR-> (in OUT->) of the HACKIT library. He send me a new version of ->DIR (complete ML), which is very fast, uses less memory and handles 0-IDs correctly. Also thanks for many suggestions and exiting talks. 2) Joseph K. Horn Thanks for suggestions, the `HP 48 Resource Allocation Guideline: Library ID's` and SORTLS (I used the kernel of it in LIBp to sort the listing by address). 3) Simone Rapisarda Thanks for many suggestions, leading to some of the advanced commands since version 1.5. 4) Romain Desplats, Georg Hoppen Thanks for suggestions and for testing all the beta releases. 4) Chris Spell Thanks for sorting this chaos :-) 5) W. C. Wickes, HP Corvallis Thanks for the HP48, the RPL tools and the ASC stuff. Happy programing, 8-Detlef. END_DOC A little example, assumes <-LIB-> is installed already: BEGIN_RPL xmpl_dm.dir %%HP: T(3); DIR SETUP \<< CLLCD "Creating:\010Workspace" 1 DISP VARS '\Gt' DUP CRDIR EVAL LIST\-> 2 SWAP START DUP RCL SWAP STO NEXT DROP "Variables" 2 DISP MCFG MD\->L 'ld\->' STO ML\->D '\->ld' STO "Library" 2 DISP D\->LIB "Cleanup\010\010\010\010" 1 DISP UPDIR '\Gt' PGDIR UPDIR "Installing library" 1 DISP IFERR :0: 888 PGLIB THEN DROP END 0 STLIB "Creating info" 1 DISP :0: 888 RLIB 888 LIBp 888 MENU "Press 'Write',\010then 'Read' \031\010\010\010\010" 1 DISP 3 FREEZE \>> $ROMID 888 $TITLE "XMPL :0.0\169XYZ'92" $VISIBLE { Read Write } $MESSAGE { "Can't find MYpar" "MYpar is invalid" "Canceled" } Read \<< IFERR 'MYpar' RCL THEN DROP # 37801h DOERR END IFERR ld\-> THEN DROP # 37802h DOERR END LIST\-> DROP CLLCD 1 DISP 3 FREEZE \>> Write \<< "Edit your text:" { 0 \Ga } IFERR 'MYpar' RCL ld\-> LIST\-> DROP THEN DROP "8-) 3-) |-} ;-\254" END + IFERR INPUT THEN DROP2 # 37803h DOERR END 1 \->LIST \->ld 'MYpar' STO \>> END END_RPL