Luc Pauwels

HP28 resident disassembler

Paul Dale
(c) 1989

From: grue@lance.hss.bu.oz (Frobozz)
Newsgroups: comp.sys.handhelds
Subject: HP28 resident disassembler
Message-ID: <3109@lance.hss.bu.oz>
Date: 14 Dec 89 06:00:35 GMT
Reply-To: grue@lance.hss.bu.oz (Frobozz)
Organization: Language Centre, Bond University, Australia.
Lines: 425

hiya,

My next offering to the net: a resident disassembler for the HP-28S. I've used Alonzo Gariepy's instruction set (posted recently) throughout with the minor change of @X to (X). Constants are secified as #digits for decimal and #$hex-deigits for hexidecimal notation. Illegal (i.e. instructions I don't know what they do) instructions are not handled correctly and may cause the program to not run correctly. Tough luck!! I'll fix this problem when it starts annoying me too much.

To use the program: store the address to start dissembly in the variable PC. The address should be a binary integer (i.e. #.... format). The program will automatically increment PC as instructions as disassembled. To disassemble a single instruction, run the program IN. The format of the output is:

  xxxx yyyyy zzzzzzz
where xxxx is the address of the instruction; yyyyy is the operation.field and zzzzzzz is the arguments of the instruction. The output is as a single string on the top of stack.

Branch instructions correctly (?) figure out what the target address is and output that. Hopefully, there are no bugs in the program (famous last words). If you discover any bugs could I be informed about it/them so I can repair the damage. If there were errors in the original instruction set listings then they are faithfully reproduced here. (I have some doubts about some of the instructions starting 81A??q where q > 7, but since I've got no better information to go by......)

The GETN GETB GET3 GETW and GETA rotutines could all be improved by coding the actual memory reference in machine code. This would improve speed quite considerably. Just recoding GETN would improve performace quite a lot. If anybody does this, I'd really like to receive a copy of the new routines.

Also, the program could be made to output to a printer (I don't have one so I am not going to do this). A better output to the screen would also be nice as well as a better user interface (i.e. #addr #length DISAS sounds like a good one then run it through my string display program [ posted ages ago ]). I have some ideas in this respect but haven't had time to implement them yet. (I wanted to get this out before Christmas so all you die-hard hackers can have some fun with the roms)

This program is copyright (C) 1989 Paul Dale. All rights reserved. Any non-profit usage of this program is permitted. Any other usage requires my permission. You will probably also require the permission of Alonzo Gariepy who posted the instruction set upon which this is based.

Paul Dale
seeya
SNIF

Language Centre              internet    : grue@lance.hss.bu.oz{.au}
Bond University              JANET       : grue%lance.hss.bu.oz@uk.ac.ukc
Gold Coast, Qld 4229         ARPA, bitnet: grue%lance.hss.bu.oz.au@uunet.uu.net
Australia                    UUCP        : ..!uunet!munnari!lance.hss.bu.oz!grue

IN [ F708 ]               ; decode one instruction from PC
  << PC HX-> ": " +
      { IN0 IN1 IN2 IN3 IN4 IN5 IN 6 IN7 IN8 IN9 INA INB INC IND INE INF }
      GETN 1 + GET PARSE +
  >>
 
FIELDS [ A74F ]           ; legal fields inside instructions
  << { ".p" ".wp" ".xs" ".x" ".s" ".m" ".b" ".w" ".?" ".?" ".?" ".?" ".?"
       ".?" ".?" ".a" } SWAP 1 + GET
  >>
 
REGS [ F07B ]             ; table of register operands
  << { " B,A" " C,B" " A,C" " C,D" " A,B" " B,C" " C,A" " D,C" " A,A" " B,B"
       " C,C" " D,D" } SWAP 1 + GET
  >>
 
GETN [ D7E ]              ; load a nibble from memory at PC
  << PC #1h PEEK NUM 48 -
    IF DUP 9 >
    THEN 7 -
    END #1h PC + 'PC' STO
  >>
 
IN81 [ 914A ]             ; instructions starting 81
  << GETN -> a
    <<
      IF a 8 <
      THEN a 4 < "RLN.w " "RRN.w " IFTE a 4 MOD 65 + CHR +
      ELSE
        IF a 11 <
        THEN GETN FIELDS {
          << GETN SWAP OVER 7 > "SUB" "ADD" IFTE SWAP + " #" + GETN 1 + ->STR +
              "," + SWAP 4 MOD 65 + CHR +
          >>
          << "SRB" SWAP + " " + GETN 65 + CHR + >>
          << GETN GETN
            IF DUP 8 >
            THEN 9 - ->STR "C"
            ELSE ->STR "A"
            END ROT {
              << ROT "MOVE" SWAP + " " + SWAP + ",R" + SWAP + >>
              << ROT "MOVE" SWAP + " R" + ROT + "," + SWAP + >>
              << ROT "SWAP" SWAP + " " + SWAP + ",R" + SWAP + >>
            } SWAP 1 + GET
          >> } a 7 - GET
        ELSE
          IF a 11 >
          THEN "SRB.w " 53 a + CHR +
          ELSE { "JUMP.a A" "JUMP.a C" "MOVE.a PC,A" "MOVE.a PC,C"
                    "SWAP.a A,PC" "SWAP.a C,PC" } GETN 1 - GET
          END
        END
      END
    >>
  >>
 
I808 [ D0B ]              ; instructions starting 808
  << { "INTON" "RSI"
        << GETN 1 + -> n
          << "MOVE.p" n ->STR + " #$" + 1 n
            START GETN R->B
            NEXT
            IF n 1 >
            THEN 1 n OVER -
              START SL SL SL SL OR
              NEXT
            END HX-> + ",A" +
          >>
        >> "BUSCB"
        << "CLRB #" GETN ->STR + ",A" + >>
        << "SETB #" GETN ->STR + ",A" + >>
        << "BRBC #" GETN ->STR + ",A" + JPB >>
        << "BRBS #" GETN ->STR + ",A" + JPB >>
        << "CLRB #" GETN ->STR + ",C" + >>
        << "SETB #" GETN ->STR + ",C" + >>
        << "BRBC #" GETN ->STR + ",C" + JPB >>
        << "BRBS #" GETN ->STR + ",C" + JPB >>
        "JUMP.a (A)" "BUSCD" "JUMP.a (C)" "INTOFF"
    } GETN 1 + GET
  >>
 
IN80 [ C680 ]             ; all instructions starting with the byte 80
  << { "OUT.s C" "OUT.x C" "IN.4 A" "IN.4 C" "UNCNFG" "CONFIG" "MOVE.a ID,C"
        "SHUTDN" I808 "ADD.a P+1,C" "RESET" "BUSCC"
        << "MOVE.1 P,C," GETN ->STR + >>
        << "MOVE.1 C," GETN ->STR + ",P" + >>
        "SREQ"
        << "SWAP.1 P,C," GETN ->STR + >>
    } GETN 1 + GET
  >>
 
IN9F [ 95CF ]             ; process branch instructions (common code)
  << GETN -> a
    <<
      IF
      THEN { "BRGT" "BRLT" "BRGE" BRLE" } a 4 / IP 1 + GET SWAP + 4 a OVER MOD
        + REGS
      ELSE
        IF a 8 <
        THEN { "BREQ" "BRNE" } a 4 / IP 1 + GET SWAP + 4 a OVER MOD + REGS
        ELSE { "BRZ" "BRNZ" } a 4 / IP 1 - GET SWAP + " " + a 4 MOD 65 + CHR
        END
      END + JPB
    >>
  >>
 
IN9 [ 730D ]              ; instructions starting with a 9 nibble
  << GETN DUP 8 MOD FIELDS SWAP 7 > IN9F
  >>
 
HWFLGS [ BEB3 ]           ; concatenate the hardware flags oin intruction
  << GETN R->B -> f
    <<
      IF f #1h AND B->R
      THEN " XM" +
      END
      IF f #2h AND B->R
      THEN " SB" +
      END
      IF f #4h AND B->R
      THEN " SR" +
      END
      IF f #8h AND B->R
      THEN " MP" +
      END
    >>
  >>
 
IN8 [ 23F5 ]              ; instructions starting with the nibble 8
  << { IN80 IN81
      << "CLRB" HWFLGS >>
      << "BRBC" HWFLGS JPB >>
      << "CLRB #" GETN ->STR + ",ST" + >>
      << "SETB #" GETN ->STR + ",ST" + >>
      << "BRBC #" GETN ->STR + ",ST" + JPB >>
      << "BRBS #" GETN ->STR + ",ST" + JPB >>
      << "BRNE.1 P,#" GETN ->STR + JPB >>
      << "BREQ.1 P,#" GETN ->STR + JPB >>
      << ".a" 0 IN9F >>
      << ".a" 1 IN9F >>
      << "JUMP.4 " PC B->R GETW DUP
        IF 32767 >
        THEN 65536 -
        END + R->B #FFFFFh AND HX-> +
      >>
      << "JUMP.a " GETA R->B HX-> + >>
      << "CALL.4 " GETW DUP
        IF 32767 >
        THEN 65536 -
        END PC B->R + R->B #FFFFFh AND HX-> +
      >>
      << "CALL.a " GETA R->B HX-> + >>
    } GETN 1 + GET
  >>
 
JPB [ 8908 ]              ; process byte offset for jumps
  << "," + PC B->R GETB DUP
    IF 127 >
    THEN 256 -
    END + R->B #FFFFFh AND HX-> +
  >>
 
IN13 [ EBB ]              ; process instructions starting with the byte 13
  << GETN R->B -> a
    << a #2h AND B->R "SWAP" "MOVE" IFTE a #8h AND B->R ".4 " ".a " IFTE +
        a #4h AND B->R "C,D" "A,D" IFTE + a #1h AND B->R ->STR +
    >>
  >>
 
INK [ 3E93 ]              ; ADD DEC
  << GETN -> a
    <<
      IF a 11 >
      THEN "DEC" SWAP + " " + a 53 + CHR
      ELSE "ADD" SWAP + a 7 > a 4 - a 4 < a DUP 4 + IFTE IFTE REGS
      END +
    >>
  >>
 
INP [ 3E51 ]              ; CLR MOVE SWAP
  << GETN -> a
    <<
      IF a 11 >
      THEN "SWAP" SWAP + a 12 - REGS
      ELSE
        IF a 4 <
        THEN "CLR" SWAP + " " + a 65 + CHR
        ELSE "MOVE" SWAP + a 4 - REGS
        END
      END +
    >>
  >>
 
INQ [ 83F7 ]              ; SUB INC SUBN
  << GETN -> a
    <<
      IF a 11 >
      THEN "SUBN" SWAP + a 12 - REGS
      ELSE
        IF a 3 > a 8 < AND
        THEN "INC" SWAP + " " + 61 a + CHR
        ELSE "SUB" SWAP + a DUP 7 > 4 0 IFTE - REGS
        END
      END +
    >>
  >>
 
INR [ 4B5C ]              ; SLN SRN NEG NOT
  << GETN DUP 4 / IP 1 + { "SLN" "SRN" "NEG" "NOT" } SWAP GET ROT + " " + SWAP
      4 MOD 65 + CHR +
  >>
 
INF [ 8256 ]              ; decode all instructions that start with F
  << ".a" INR
  >>
 
INE [ 824E ]              ; decode all that start with E
  << ".a" INQ
  >>
 
IND [ 8246 ]              ; decode all that start with D
  << ".a" INP
  >>
 
INC [ 829E ]              ; decode all that start with C
  << ".a" INK
  >>
 
INB [ 47F4 ]              ; decode all that start with B
  << GETN DUP 8 MOD FIELDS SWAP
    IF 7 >
    THEN INR
    ELSE INQ
    END
  >>
 
INA [ 73D4 ]              ; decode all that start with A
  << GETN DUP 8 MOD FIELDS SWAP
    IF 7 >
    THEN INP
    ELSE INK
    END
  >>
 
IN7 [ F7F ]               ; handle CALL.3
  << "CALL.3 " GET3 DUP
    IF 2047 >
    THEN 4096 -
    END PC B->R + R->B #FFFFFh AND HX-> +
  >>
 
IN6 [ 76C2 ]              ; process the JUMP.3 instructions
  << "JUMP.3 " PC B->R GET3 DUP
    IF 2047 >
    THEN 4096 -
    END + R->B #FFFFFh AND HX-> +
  >>
 
IN5 [ 1F3B ]              ; process the BRCC instructions
  << "BRCC " PC B->R GETB DUP
    IF 127 >
    THEN 256 -
    END + R->B #FFFFFh AND HX-> +
  >>
 
IN4 [ 1F3A ]              ; process the BRCS instructions
  << "BRCS " PC B->R GETB DUP
    IF 127 >
    THEN 256 -
    END + R->B #FFFFFh AND HX-> +
  >>
 
IN3 [ 73B3 ]              ; decode the MOVE.Pn instructions
  << GETN 1 + -> n
    <<  "MOVE.p" n ->STR + " #$" + 1 n
      START GETN R->B NEXT
      IF n 1 >
      THEN 1 n OVER -
        START SL SL SL SL OR
        NEXT
      END HX-> + ",C" +
    >>
  >>
 
HX-> [ 5236 ]             ; convert a real into a hex string
  << ->STR 3 OVER SIZE 1 - SUB
  >>
 
GETHA [ A17A ]            ; get address as hex string
  << GETA R->B HX->
  >>
 
IN1XY [ F422 ]            ; decode addressing mode for 14 15 instructions
  << { "A,(D0)" "A,(D1)" "(D0),A" "(D1),A" "C,(D0)" "C,(D1)" "(D0),C" "(D1),C" }
      GETN SWAP OVER 8 MOD 1 + GET SWAP 8 <
  >>
 
IN1JK [ B069 ]            ; decode register for 10 11 12 instructions
  << GETN DUP 8 < "A" "C" IFTE SWAP 8 MOD ->STR
  >>
 
IN1 [ 511 ]               ; process all instructions starting with 1
  << {
      << IN1JK "MOVE.w " ROT + ",R" + SWAP + >>
      << IN1JK "MOVE.w R" SWAP + "," + SWAP + >>
      << IN1JK "SWAP.w " ROT + ",R" + SWAP + >>
      IN13
      << IN1XY "MOVE.a " "MOVE.b " IFTE SWAP + >>
      <<
        IF IN1XY
        THEN GETN FIELDS
        ELSE "." GETN 1 + ->STR +
        END "MOVE" SWAP + " " + SWAP +
      >>
      << "ADD.a #" GETN 1 + ->STR + ",D0" + >>
      << "ADD.a #" GETN 1 + ->STR + ",D1" + >>
      << "SUB.a #" GETN 1 + ->STR + ",D0" + >>
      << "MOVE.2 #" GETB ->STR + ",D0" + >>
      << "MOVE.4 #" GETW ->STR + ",D0" + >>
      << "MOVE.5 #$" GETHA ->STR + ",D0" + >>
      << "SUB.a #" GETN 1 + ->STR + ",D1" + >>
      << "MOVE.2 #" GETB ->STR + ",D1" + >>
      << "MOVE.4 #" GETW ->STR + ",D1" + >>
      << "MOVE.5 #$" GETHA ->STR + ",D1" + >>
    } GETN 1 + GET
  >>
 
IN2 [ 23DC ]              ; process all instructions with 2 as first nibble
  << "MOVE.1 #" GETN ->STR + ",P" +
  >>
 
PARSE [ AC79 ]            ; keep eval'ing top of stack until a string appears
  << -> z
    <<
      WHILE z TYPE 2 =/=
      REPEAT z EVAL 'z' STO
      END z
    >>
  >>
 
IN0 [ 8AE8 ]              ; decode all instructions starting with 0 nibble
  << { "RETSETXM" "RET" "RETSETC" "RETCLRC" "SETHEX" "SETDEC"
        "PUSH.a C" "POP.a C" "CLR.x ST" "MOVE.x ST,C" "MOVE.x C,ST"
        "SWAP.x C,ST" "INC.1 P" "DEC.1 P"
        << GETN FIELDS GETN DUP 7 > "AND" "OR" IFTE ROT + SWAP 8 MOD REGS + >>
        "RETI"
    } GETN 1 + GET
  >>
 
GET3 [ 869E ]             ; load a 12 bit thing from PC
  << GETB GETN 256 * +
  >>
 
GETA [ 25C0 ]             ; load a 20 bit pointer from PC
  << GETW GETN 65536 * +
  >>
 
GETW [ E69E ]             ; load a 16 bit thing from PC
  << GETB GETB 256 * +
  >>
 
GETB [ 44D4 ]             ; load a byte from PC
  << GETN GETN 16 * +
  >>