HP-71 File Structure From "The Titan File" by Joe Horn If you'd like to go spelunking through HP-71 files, but aren't sure what's where, here's everything you need to know, all in one handy place. You'll need a copy of PEEKUTIL, written by Flavio Casetta. First, assuming the file we wish to explore is called FRED, then do this: +----------------------+ | A=HTD(ADDR$("FRED")) | +----------------------+ This puts FRED's address into A. If you change memory in any way that might move FRED, be sure to recalculate A in the way shown above. Now, for any filetype at all: TEXT$(A,8) = "FRED " (full 8-character file name) RPEEK$(A+16,4) = FRED's filetype in hex RED(PEEK(A+16,4),2^15) = FRED's filetype in signed decimal PEEK(A+20) = FRED's file protection as follows: 0 = no protection 1 = SECURE only 2 = PRIVATE only 3 = EXECUTE ONLY (both SECURE and PRIVATE) RPEEK$(A+22,4) = FRED's creation time in HHMM format RPEEK$(A+26,6) = FRED's creation date in YYMMDD format PEEK(A+32,5)+32 = FRED's total length in nibbles ADPEEK$(A+32,5) = Hex address of file after FRED in CATalog ADPEEK(A+32,5) = Decimal address of file after FRED in CATalog +--------------------------+ | If FRED is an SDATA file | +--------------------------+ PEEK(A+32,5) DIV 16 = number of records in FRED RPEEK$(A+37+16*N,16) = Contents of Nth record (record 0 is first) +------------------------+ | If FRED is a DATA file | +------------------------+ PEEK(A+37,4) = Number of records in FRED PEEK(A+41,4) = Number of bytes in each record in FRED +-----------------------+ | If FRED is a LEX file | +-----------------------+ PEEK(a+37,2) = FRED's lex ID in decimal RPEEK$(A+37,2) = FRED's lex ID in hex PEEK(A+39,2) = FRED's lowest token number in decimal RPEEK$(A+39,2) = FRED's lowest token number in hex PEEK(A+41,2) = FRED's highest token number in decimal RPEEK$(A+41,2) = FRED's highest token number in hex PEEK(A+43,5) = Distance to next linked lex table (0 if none) If that's not 0, then: ADPEEK(A+43,5) = decimal address of next linked lex table ADPEEK$(A+43,5) = hex address of next linked lex table (NB: Linked lex tables are all within one lexfile) PEEK(A+48) = Speed Table existence flag (15 if none) If that's 0, then LET A=A+79 to skip over speed table PEEK(A+49,4) = Distance to Text Table (0 if none) If that's not 0, then: ADPEEK(A+49,4,-1) = decimal address of text table ADPEEK$(A+49,4,-1) = hex address of text table PEEK(A+53,4) = Distance to Message Table (0 if none) If that's not 0, then: ADPEEK(A+53,4) = decimal addresss of message table ADPEEK$(A+53,4) = hex addresss of message table PEEK(A+57,5) = Distance to Poll Handler (0 if none) If that's not 0, then: ADPEEK(A+57,5) = decimal address of poll handler ADPEEK$(A+57,5) = hex address of poll handler A+62 = decimal address of Main Table +-------------------------+ | If FRED is a BASIC file | +-------------------------+ RPEEK$(A+37,5) = Hex distance to the first SUB or END SUB If 00000, then RUN or RENUMBER and try again If FFFFF, then there are no SUBs nor END SUBs RPEEK$(A+42,5) = Hex distance to first label or DEF FN If 00000, then RUN or RENUMBER and try again If FFFFF, then there are no labels nor DEF FNs RPEEK$(A+47,2) = "F0", the End-Of-Line (EOL) marker VAL(RPEEK$(A+49,4)) = first line number PEEK(A+53,2)-2 = length of first statement in nibbles If you LET L=PEEK(A+53,2)-2, then: PEEK$(A+55,L) = hex contents of first statement RPEEK$(A+55+L,2) = EOL or @ If F0, it's an EOL followed by another line number If F4, it's an @ followed by another length byte and so on, until the end of the file is reached. With these tools, you can explore any type of file. Here are a few ideas that came from this (which also require the PEEKUTIL lexfile): +---------------------------+ | TOKENIZE in one keystroke | +---------------------------+ In the CHHU Chronicle, V1 N3 P29, a program called TOKENIZE was listed, which enabled you to see how BASIC changes keywords into hex tokens internally. Here is the same idea, but on a simple key definition instead of a whole program: KEY 'f4','J=PEEK(193885,5)@I=PEEK(J+53,2)-2@PEEK$(J+55,I);" (";STR$(I);")"': After making this key assignment, merely pressing [f][4] (the SIN key) will show the tokenized version of the first statement of the current BASIC file, no matter what it is. This is followed by a nibble count, in parentheses. This is useful in comparing the byte length of different approaches to the same task. +---------------------------+ | LEXCAT lexfile catalogger | +---------------------------+ Run LEXCAT (or CALL LEXCAT(F$) with a filename in F$), and see the hex address of the specified lexfile, the address of its poll handler(s), the address of the next file, and all the keywords in the lexfile with their execution code address (entry point), lex ID and token number, and function syntax. This is an invaluable program for those of us who use lexfiles a lot. It is based on a program by HP, with a few more bells and whistles, cleaner output, and much faster thanks to PEEKUTIL. 5 ! Requires PEEKUTIL & HPILROM 10 INPUT 'LEX File: ';F$ @ CALL LEXCAT(F$) 20 SUB LEXCAT(F$) @ ON ERROR GOTO 290 @ A1=HTD(ADDR$(F$)) 30 IF PEEK(A1+16,2)#8 THEN DISP "ERR:Not LEX file" @ END 40 S=A1+37 @ F$=UPRC$(F$) 50 C=S+25+NOT PEEK(S+11)*79 @ T$=DTH$(C)&RPEEK$(S,6) 80 IF LEN(F$) THEN PRINT @ PRINT "*** ";F$;" ***" @ PRINT "(";DTH$(A1);") File Header" 90 M=HTD(T$[8,9]) @ B=ADPEEK(C-13,4) 100 IF PEEK(C-5,5) THEN PRINT "(";ADPEEK$(C-5,5);") Poll Handler" 120 IF PEEK(B,2)=255 THEN 300 130 L=PEEK(B-1)+1 @ W$=TEXT$(B,L/2) @ S$=RPEEK$(B+L,2) @ D=C+(HTD(S$)-M)*9 140 PRINT "(";ADPEEK$(D+3,5);") ";T$[10];"/"; @ B=B+L+3 150 IF NOT HTD(S$) THEN PRINT "-- ";W$;" ffn" @ GOTO 110 160 PRINT S$;" ";W$; @ E=PEEK(D+8) @ IF NOT E THEN PRINT " postfix" @ GOTO 110 170 IF E#15 THEN 250 180 E=RMD(ADPEEK(D+3,5,-2),1048576) @ P=PEEK(E) @ Q=PEEK(E+1) 190 IF Q THEN PRINT "("; ELSE PRINT " fn" @ GOTO 110 200 FOR I=1 TO Q @ IF I#1 THEN PRINT ","; 210 E=E-1 @ X=PEEK(E) @ IF I>P THEN PRINT "?"; 220 IF X>11 THEN PRINT "#/$"; ELSE IF X>7 THEN PRINT "#"; ELSE PRINT "$"; 230 IF RMD(X,4) THEN PRINT "()"; 240 NEXT I @ PRINT ")" @ GOTO 110 250 IF NOT BIT(E,3) THEN PRINT " *P"; 260 IF NOT BIT(E,2) THEN PRINT " *I"; 270 IF NOT BIT(E,0) THEN PRINT " *K"; 280 PRINT @ GOTO 110 290 DISP "ERR:";ERRM$ @ BEEP @ END 300 IF KEY$="#38" THEN END 310 IF NOT HTD(T$[10]) THEN END 330 IF PEEK(S+6,5) THEN S=ADPEEK(S+6,5) @ F$="" @ GOTO 50 340 DISP "(";ADPEEK$(A1+32,5);") Next File Header" 350 END SUB If run on SPLEX, you'll see (with varying addresses): *** SPLEX *** (80008) File Header (80077) Poll Handler (800D5) 52/24 LIST$(#,?$) (80281) 52/23 MODIFY (808C4) Poll Handler (80900) 53/55 MAXRC fn (809F1) Next File Header which means SPLEX's file header begins at hex address 80008; its first poll handler begins execution at hex 80077; the LIST$ function starts execution at hex address 800D5, it has lex ID 52 (hex), token 24 (hex), and takes one numeric argument, followed by an optional string argument; the MODIFY keyword is a statement (no clue to its syntax!); there's another poll handler at 808C4; MAXRC is a function with no arguments; and the file ends at 809F0.