Post Reply 
Library data object
08-29-2019, 11:43 PM
Post: #1
Library data object
Hello all,

For a programming project on the HP-50g I want to know how to create and read out a library data object. This way I can protect the data that is created by the library from being corrupted. Does anyone know how to do this in System RPL and want to share this with me? Thanks in advance! Sincerely, Karel.

I use HP-16C, WP-34S emulator, HP-35s, HP-48GX, HP-50g, and HP Prime G2.
Find all posts by this user
Quote this message in a reply
08-31-2019, 04:23 AM (This post was last modified: 12-29-2020 07:51 AM by Giuseppe Donnini.)
Post: #2
RE: Library data object
[ WARNING: The following information refers to the HP-48; it may (and probably does) apply without changes to the HP-50G, but no warranty is given. ]


A Library Data object (or libdat for short) is essentially an encapsulated raw memory buffer with no convention imposed on the programmer other than that associated with variable-length atomic data class objects, namely that the body must begin with a 5-nibble length field:


                      +--------------------+
                      |   --> DOEXT0       | Prologue Address
                      +--------------------+
                      |                    |            Library
                      |    Length Field    |            Data
                      |    ------------    | Body       Object
                      |   Nibble Sequence  |
                      |                    |
                      +--------------------+


The RPL kernel does not even require a libdat to identify the library for which it is supposed to provide a buffer! So, in the end, it all depends on yourself and how you want to store and access the data. To this effect, you may establish any protocol you please, because you will be the only one needing to follow it.

However, to avoid conflicts between applications, HP themselves use the following convention, which ensures that a given library can only access those libdats that are intended for its own use:


                   +----------------------------+
                   |      --> DOEXT0        (5) |   Prologue Address
                   +----------------------------+
                   |                            |
                   |       Length Field     (5) |
                   |      ---------------       |
                   |        Library ID      (5) |
                   |      ---------------       |
             ,---- |   +------------------+     |              Library
            /      |   |  --> DO...       | (5) |              Data
    Encapsulated   |   +------------------+     |   Body       Object
     RPL Object    |   |                  |     |
   (often a list)  |   |    Object Body   | (n) |
            \      |   |                  |     |
             '---- |   +------------------+     |
                   |      ---------------       |
                   |       --> SEMI         (5) |
                   |                            |
                   +----------------------------+


Apart from the 10 predefined nibbles of a libdat (prologue address and length field), 10 extra nibbles are reserved in addition to the encapsulated object:
— 5 for the library ID, immediately following the length field;
— and 5 for a pointer to SEMI, at the very end of the object.

Whenever the encapsulated object needs to be recalled, the libdat is first transformed into a two-element list containing, as its first element, the library ID in form of a system binary integer, and, as its second element, the original encapsulated object.


                   +----------------------------+
                   |      --> DOLIST        (5) |   Prologue Address
                   +----------------------------+
                   |                            |
             ,---- |   +------------------+     |
            /      |   |  --> DOBINT      | (5) |
           /       |   +------------------+     |
        First      |   |                  |     |
    List Element   |   |      Number      |     |
           \       |   |   (Library ID)   | (5) |
            \      |   |                  |     |
             '---- |   +------------------+     |              List
                   |                            |   Body       Object
             ,---- |   +------------------+     |
            /      |   |  --> DO...       | (5) |
        Second     |   +------------------+     |
     List Element  |   |                  |     |
    (often a list) |   |    Object Body   | (n) |
            \      |   |                  |     |
             '---- |   +------------------+     |
                   |      ---------------       |
                   |       --> SEMI         (5) |
                   |                            |
                   +----------------------------+


This type conversion is achieved with minimal effort by:
  1. overwriting the libdat's prologue address with that of a list (note that the prior presence of a pointer to SEMI automatically leads to the creation of a valid list object);
  2. overwriting the length field with the prologue address of a bint, thereby transforming the library ID value into a full-blown RPL object contained within the list.



     +----------------------------+              +----------------------------+
     |      --> DOEXT0        (5) | >>>>>>>>>>>> |      --> DOLIST        (5) |
     +----------------------------+              +----------------------------+
     |                            |              |                            |
     |                            |              |   +------------------+     |
     |       Length Field     (5) | >>>>>>>>>>>> |   |  --> DOBINT      | (5) |
     |                            |              |   +------------------+     |
     |      ---------------       |              |   |                  |     |
     |                            |              |   |      Number      |     |
     |        Library ID      (5) |              |   |   (Library ID)   | (5) |
     |                            |              |   |                  |     |
     |      ---------------       |              |   +------------------+     |
     |                            |              |                            |
     |   +------------------+     |              |   +------------------+     |
     |   |  --> DO...       | (5) |              |   |  --> DO...       | (5) |
     |   +------------------+     |              |   +------------------+     |
     |   |                  |     |              |   |                  |     |
     |   |    Object Body   | (n) |              |   |    Object Body   | (n) |
     |   |                  |     |              |   |                  |     |
     |   +------------------+     |              |   +------------------+     |
     |      ---------------       |              |      ---------------       |
     |       --> SEMI         (5) |              |       --> SEMI         (5) |
     |                            |              |                            |
     +----------------------------+              +----------------------------+


Decomposition of the list will leave the library ID on the stack (in form of a bint), which may then be tested against the ID of the library trying to access the libdat.

All you need, therefore, is a pair of two utility programs which convert, respectively, HP-style libdats into lists, and lists back into HP-style libdats. Here's one possible implementation of such a system:



*******************************************************************************
NULLNAME >LIBDAT
*******************************************************************************
* STACK  : ( ob #romID --> libdat )
*
* DETAIL : No argument checking is performed as it is assumed that >LIBDAT will
*          only be used by System RPL programmers.
*******************************************************************************
::
 SWAP TWO{}N   ( { #romID ob } --> )
 DUP OSIZE     ( { #romID ob } #listSize --> ) ( *OSIZE includes prolog addr* )

 CODE
        GOSBVL  =POP#   * Pop #listSize from stack & put its value into A[A].
        C=A     A       * Copy list size to C[A].
        RSTK=C          * Save list size on return stack.
        A=DAT1  A       * Fetch address of list [new top of stack after POP#].
        AD1EX           * Point to list ; A[A]=former D1.
        LC(5)   =DOEXT0 * Load libdat prolog address into C[A].
        DAT1=C  A       * Overwrite list prolog addr with libdat prolog addr.
        D1=D1+  5       * Point to system binary integer #romID.
        C=RSTK          * Retrieve list size from return stack.
        C=C-CON A,5     * Subtract 5 to get libdat size field value.
        DAT1=C  A       * Overwrite bint prolog address with size field value.
        D1=A            * Restore D1.
        GOVLNG  =Loop   * Return to RPL.
 ENDCODE

               ( libdat )
;



*******************************************************************************
NULLNAME LIBDAT>
*******************************************************************************
* STACK  : ( libdat --> ob #romID T )   Valid HP-style libdat
*          ( libdat --> F )             Invalid libdat
*
* DETAIL : + The argument is assumed to be an actual library data object.  As
*            this routine is only intended for System RPL programmers, argument
*            checking--if needed--should be performed at a higher level.
*
*          + libdat is deemed invalid, if one of the following is true:
*            - libdat is too small to be a HP-style libdat.
*            - libdat's library ID is out of range.
*            - libdat does not contain an encapsulated object.
*            - libdat has no pointer to SEMI at its very end.
*******************************************************************************
::
 TOTEMPOB      ( *Make unique copy in tempob, leaving orig. libdat unchanged* )

 CODE

* Initialization.

        C=DAT1  A            * Fetch address of libdat (copy in tempob).
        CD1EX                * Point to libdat; C[A] holds original D1 value.
        RSTK=C               * Save original D1 value on return stack.


* Check if size of libdat is in compliance with the HP convention.

        D1=D1+  5            * Point to libdat length field.
        A=DAT1  A            * Fetch libdat length.
        C=0     A
        LC(2)   26
        ?C>A    A            * Is it 25 nibbles or less?
        GOYES   exit         * Yes, then exit and signal FALSE.


* Check if library ID is in a valid range.

        D1=D1+  5            * Point to library ID.
        A=DAT1  A            * Fetch library ID.
        LC(5)   #007FE
        ?A>C    A            * Is it greater than the highest possible ID?
        GOYES   exit         * Yes, then exit and signal FALSE.


* Check if there is an encapsulated object (rough check only).
* [ N.B. This is for the HP-48; for the HP-50G, replace =DOBINT with =DOINT. ]

        D1=D1+  5            * Point to encapsulated object.
        A=DAT1  A            * Fetch prolog address of encapsulated object.
        LC(5)   =DOBINT      * [ ! Replace with =DOINT for the HP-50G ! ]
        ?A<C    A            * Is it lower than lowest possible prolog address?
        GOYES   exit         * Yes, then exit and signal FALSE.
        LC(5)   =DOROMP
        ?A>C    A            * Is it higher than highest possible prolog addr?
        GOYES   exit         * Yes, then exit and signal FALSE.


* Check if there is a pointer to SEMI at the very end.

        D1=D1-  10           * Point back to libdat length field.
        A=DAT1  A            * Fetch libdat length.
        CD1EX                * Put address of libdat length field into C[A].
        R0=C                 * Save a copy of that address in R0[A].
        C=C+A   A            * Calculate address just past the end of libdat.
        D1=C                 * Point to just past the end of libdat.
        D1=D1-  5            * Point to last 5 nibbles of libdat.
        A=DAT1  A            * Fetch value of last 5 nibbles of libdat.
        LC(5)   =SEMI
        ?C#A    A            * Does it match the address of SEMI?
        GOYES   exit         * No, then exit and signal FALSE.


* All checks done; convert copy of libdat to list.

        C=R0                 * Retrieve address of libdat length field.
        D1=C                 * Point to libdat length field.
        LC(5)   =DOBINT      * Load bint prolog address into C[A].
        DAT1=C  A            * Overwrite length field w/ bint prolog address.
        D1=D1-  5            * Point to libdat prolog address.
        LC(5)   =DOLIST      * Load list prolog address into C[A].
        DAT1=C  A            * Overwrite libdat prolog addr w/ that of a list.


* Clean-up and exit to RPL.

exit    C=RSTK               * Retrieve original D1 value from return stack.
        D1=C                 * Restore D1 to its original state.
        GOVLNG  =PushF/TLoop * Return FALSE if carry set; TRUE if carry clear.
 ENDCODE

 NOTcsdrpfls   ( F ) ( *If invalid, drop libdat and return FALSE* )

 INCOMPDROP    ( #romID ob --> )
 SWAPTRUE      ( ob #romID T )
;



Another possibility would be to hard-code the library IDs directly into the conversion programs so that they are out of the way (like the HP-48 itself does in the Equation Library). But in that case you would have to create a different pair of conversion programs for every single library you want to build. That's why I preferred a more general approach for the present illustration.

One final note: The HP convention does not allow raw data to be directly included in a libdat. In fact, during the conversion of an HP-libdat, its encapsulated part becomes an element of an RPL composite, which may only contain other valid RPL objects (or pointers thereto). One workaround for this problem would be to encapsulate the raw data in a hex string before—in turn—encapsulating the hex string in an HP-libdat.
Find all posts by this user
Quote this message in a reply
09-02-2019, 10:47 PM
Post: #3
RE: Library data object
Dear Sir Donnini,

I would like to thank you for this contribution. I have not yet had the opportunity to test this, but I will certainly do it. Thank you! Sincerely, Karel.

I use HP-16C, WP-34S emulator, HP-35s, HP-48GX, HP-50g, and HP Prime G2.
Find all posts by this user
Quote this message in a reply
10-06-2019, 06:32 PM
Post: #4
RE: Library data object
Dear Sir Donnini,

There are a number of errors in the code that MASD cannot handle, see below. Maybe you can help me with this. Sincerely, Karel.


In -> libdat:
C=C-CON A,5
LC(5) =DOEXT0

In libdat->:
C=C+A A
D1=C
LC(5) =DOLIST
LC(5) =DOBINT
LC(5) =SEMI
LC(5) =DOROMP
LC(5) #007FE

I use HP-16C, WP-34S emulator, HP-35s, HP-48GX, HP-50g, and HP Prime G2.
Find all posts by this user
Quote this message in a reply
10-08-2019, 09:38 PM
Post: #5
RE: Library data object
Suppose you have a library that needs to use a LIBDATA object. Where do you store it? Does the library config routine put it in the hidden directory? Or does it go somewhere else?

I always thought it was very strange that there was no easy provision for libraries to have their own data. Or am I missing some way to do that?

Dave
Find all posts by this user
Quote this message in a reply
10-09-2019, 11:49 AM (This post was last modified: 10-09-2019 01:31 PM by Giuseppe Donnini.)
Post: #6
RE: Library data object
I don't think there are any errors in my code. Everything works as expected on an HP-48 and I clearly stated that my explanations do not cover the HP-50G. If you want to use the code on that machine, you should download the "HP-50G Advanced User's Reference Manual" (https://www.hpcalc.org/details/7141) and read the chapter on MASD syntax. You will then be able to seamlessly translate my code.

>LIBDAT in MASD syntax:

Code:
"::
SWAP
TWO{}N 
DUP
OSIZE   
CODE
        GOSBVL 06641
        C=A.A 
        RSTK=C
        A=DAT1.A 
        AD1EX
        LC 02B88
        DAT1=C.A 
        D1+5
        C=RSTK
        C-5.A 
        DAT1=C.A 
        D1=A 
        GOVLNG 05149
ENDCODE
;
@"

LIBDAT> in MASD syntax:

Code:
"::
TOTEMPOB
CODE
        C=DAT1.A 
        CD1EX
        RSTK=C
        D1+5
        A=DAT1.A 
        C=0.A 
        LC 1A
        ?C>A.A   ->exit
        D1+5     
        A=DAT1.A  
        LC 007FE 
        ?C<A.A   ->exit
        D1+5
        A=DAT1.A 
        LC 02614
        ?C>A.A   ->exit
        LC 02E92
        ?C<A.A   ->exit
        D1-10
        A=DAT1.A 
        CD1EX
        R0=C.W 
        C=C+A.A 
        D1=C 
        D1-5
        A=DAT1.A 
        LC 0312B
        ?C#A.A   ->exit
        C=R0.W 
        D1=C 
        LC 02911
        DAT1=C.A 
        D1-5
        LC 02A74
        DAT1=C.A 
*exit   C=RSTK
        D1=C 
        GOVLNG 3523D
ENDCODE
NOTcsdrpfls
INCOMPDROP 
SWAPTRUE
;
@"

You may also install Jazz on the HP-50G (https://www.hpcalc.org/details/7196) which would allow you to compile my code exactly as written.
Find all posts by this user
Quote this message in a reply
10-09-2019, 12:57 PM (This post was last modified: 10-10-2019 10:00 PM by Giuseppe Donnini.)
Post: #7
RE: Library data object
(10-08-2019 09:38 PM)David Hayden Wrote:  Suppose you have a library that needs to use a LIBDATA object. Where do you store it?

Do you mean as an end user or as a library developer?

As a user, you will necessarily have to follow the protocol established by the developer of a given library. In principle, the accompanying documentation should provide all necessary information.

As a developer, you may store library data objects wherever you want in semi-permanent RAM, i.e. in USEROB (which comprises the HOME directory, the hidden directory, and any other, ordinary, subdirectory) or in unmerged Port Memory (ports 0-2 on an HP-48SX; ports 0-33 on an HP-48GX). It is quite straightforward to code the access routines in such a way that the exact location of the variable becomes irrelevant.

(10-08-2019 09:38 PM)David Hayden Wrote:  Does the library config routine put it in the hidden directory? Or does it go somewhere else?

Per se, the library configuration routine bears no connection to the handling of library data objects. Again, it all depends on the developer, who may or may not choose to create default objects in the hidden directory (or anywhere else) during configuration time.
Find all posts by this user
Quote this message in a reply
Post Reply 




User(s) browsing this thread: