HP48 GX

ASCII encoding

ASCII encoding for Objects

I’m sharing information on the structure of HP48 ASCII headers, sourced from a FAQ available here, I decided to repost it because I used this function with success in particular to copy to emulators that do not support Kermit transfer but allow a copy paste, I can then encode the program as ASCII and put in on the stack and then it can be converted back.

My purpose in doing so is to detail a method I’ve personally found effective for transferring programs to emulators that do not support the Kermit transfer protocol but do permit copy-pasting. This involves encoding the program in ASCII format for transfer, then decoding it back on the emulator. It’s important to note that this is purely a description of what has worked for me. I am not advocating for or recommending this approach due to the potential risks involved, including the loss of memory I experienced during my attempts.

Sending an HP48 GX object via electronic mail can be difficult if the object does not have an ASCII form, such as is the case for library objects.

The programs are nominally called →ASC and ASC→. The former takes an object from the stack and converts it to a string, in which each nibble of the object and its checksum is converted to a character 0-9 or A-F. (The object must be in RAM, otherwise a “ROM Object” error is returned.) For sake of easy inclusion in e-mail letters, the string is broken up by linefeed characters after every 64 characters.

ASC\-> is the inverse of →ASC: it takes a string created by →ASC and converts it back into an object. When the encoded strings is trasmitted, the string shouldn’t be changed; ASC→ uses the checksum encoded in the string to verify that the decoding is correct. An "Invalid String" error is returned if the result object does not match the original object encoded by →ASC. When a string is uploaded to a computer, HP48 mode 3 is recommended so that the HP48 will convert any CR/LF’s back to LF’s when the string is later downloaded.

Two versions of ASC→ are listed here. The first (P1) is in HP48 user language, using SYSEVAL to execute system objects. P2 is a string that the setup program uses P1 to decode into an executable ASC→ - then P1 is discarded. The second version is more compact than the first, and also uneditable and therefore safer (but it can’t be transmitted in ASCII form, which helps to make the point of this exercise).


%%HP: T(3)A(D)F(.);
DIR
P1              @ ASC\-> Version 1.
\<<
  IF DUP TYPE 2 \=/
  THEN "Not A String" DOERR
  END RCWS \-> ws
  \<< 16 STWS
    #0 NEWOB SWAP DUP SIZE
    IF DUP 4 <
    THEN DROP SWAP DROP "Invalid String" DOERR
    END
    DUP 65 / IP - 4 - # 18CEAh SYSEVAL
    "" OVER # 61C1Ch SYSEVAL
    SWAP # 6641F8000AF02DCCh
    # 130480679BF8CC0h # 518Ah SYSEVAL
    # 19610313418D7EA4h # 518Ah SYSEVAL
    # 7134147114103123h # 518Ah SYSEVAL
    # 5F6A971131607414h # 518Ah SYSEVAL
    # 12EA1717EA3F130Ch # 518Ah SYSEVAL
    # 280826B3012808F4h # 518Ah SYSEVAL
    # 6B7028080BEE9091h # 518Ah SYSEVAL
    # BE5DC1710610C512h # 518Ah SYSEVAL
    # 705D00003431A078h # 518Ah SYSEVAL
    # 3D8FA26058961431h # 518Ah SYSEVAL
    # 312B0514h # 518Ah SYSEVAL
    # 18F23h SYSEVAL
    DUP BYTES DROP 4 ROLL
    IF ==
    THEN SWAP DROP
    ELSE DROP "Invalid String" DOERR
    END ws STWS
  \>>
\>>

P2      @ ASC\-> Version 2.  To be converted by ASC\-> version 1.

"D9D20D29512BF81D0040D9D20E4A209000000007566074726636508813011920
140007FE30B9F060ED3071040CA1304EC3039916D9D2085230B9F06C2A201200
094E66716C696460235472796E676933A1B21300ED30FD5502C230C1C1632230
CCD20FA0008F14660CC8FB97608403104AE7D814313016913213014117414317
414706131179A6F5C031F3AE7171AE214F8082103B6280821909EEB0808207B6
215C0160171CD5EB870A13430000D50713416985062AF8D341508813044950B9
F06BBF06EFC36B9F0644230C2A201200094E66716C696460235472796E676933
A1B2130B21300373"

P3      @\->ASC.     To be converted by ASC\->.
"D9D20D2951881304495032230FD5502C230A752688130ADB467FE30322306AC3
0CB916E0E30CBD30F6E30C1C1632230CCD20DC0008F14660CC8FB97608403104
AE7D8143130169174147061741431311534AC6B4415141534946908D9B026155
4A6F53131F3AE731A014C161AE215F08082103A6280821939EEC08082170A621
4C161170CD56B870A18503430000D5071351796A9F8D2D02639916D9D2085230
C2A209100025F4D402F426A6563647933A1B2130A2116B213033C0"

SETUP   @Automatic setup program
\<< P2 P1 'ASC\->' STO
    P3 ASC\-> '\->ASC' STO
    { P1 P2 P3 SETUP } PURGE
\>>

END

How I installed these functions:

  • I saved the above text into a text file named CONV.
  • I set the HP48 SX into ASCII transfer mode.
  • Using Kermit, I downloaded CONV text file to the 48, and verified its checksum (6C8Ah).
  • I executed CONV to make it the current directory.
  • I executed SETUP.
  • The directory CONV now contains ASC→ and →ASC, ready to use.

I finally archived the decoded versions of ASC→ and →ASC on my computer, setting the HP48 SX in binary transfer mode before uploading.

Checksum calculation

Many documents make references to the “checksum” of an object. This is a 16-bit user binary resulting from a CRC calculation on the contents of an object. This binary is supposed to be relatively unique, with only 1 change in 65536 of accidental equal checksums on two different objects. This allows to distinguish programs that look the same but may be quite different (even if the size of them is the same). It is also often used to verify the correct transmission of files.

It is possible to calculate the checksum of an object using the BYTES command. This will return on the stack two things - the size of the object in bytes on level 1, and the checksum on level two. Note that while the checksum of a variable name is the same as running the checksum on the object itself, the sizes will be differ by 4.5 bytes + the size of the variable name itself.

In rare cases the checksum of two objects can be the same, even if the objects are different. This is due to the limited nature of the HP48’s checksum function.