Schöner Drucker für MMIX-Assembly

  • Ich schreibe gerade einen Disassembler für Don Knuths MMIX . Das Modul in diesem gist ist ein hübscher Drucker für einzelne Anweisungen, der Anweisungen aus einem char* -Puffer einliest und druckt die resultierende Demontage nach stdout. Es ist ziemlich lang, der folgende Code ist ein Auszug der wichtigsten Teile.

    Ich bin ziemlich neu bei C. Ich würde mich sehr über Bemerkungen zu diesem Codeteil freuen verbessert werden. Ich bin besonders besorgt über Dinge, die auf anderen Architekturen als x86 (-64) oder anderen Betriebssystemen nicht funktionieren. Ab sofort habe ich bereits den ##c -Kanal von People freenode um Hilfe gebeten, er schlug mir vor, die neue C99-Syntax const char *opfstrings[] = { [OP_SX_SY_SZ] = ... , [OP_SX] = ... } und enum s zu verwenden, um zu vermeiden, dass all diese Indizes explizit geschrieben werden |>

    Tabellen wurden aus Gründen der Kürze gekürzt. Für den Rest sehen Sie den auf GitHub eingefügten Code.

    pretty_print .c

     #include <stdio.h>
    #include <inttypes.h>
    
    #include "pretty_print.h"
    
    const char *opcodes[256] = {
      "trap","fcmp","fun","feql","fadd","fix","fsub","fixu",              /* 0x0# */
      "flot","flot","flotu","flotu","sflot","sflot","sflotu","sflotu",
    
    /* ... a lot more ... */
    
      "jmp","jmp","pushj","pushj","geta","geta","put","put",              /* 0xF# */
      "pop","resume","save","unsave","sync","swym","get","trip"
    };
    
    /* kinds of arguments and format strings
     * S#:  register number, e.g. $123
     * #:   8-bit unsigned,  e.g. #1F
     * ##:  the same, 16 Bit, e.g. #1337
     * ###: the same, 24 Bit, e.g. #1F2E3D
     * F##: relative 16-bit adress, forwards,  e.g.. @+4*#1234
     * B##: the same, backwards, z.B. @-4*#1234
     * F###, B###: the same, 24 Bit
     * R#:  special register, z.B. rH
     * ROUND: rounding mode, string contains comma
     */
    enum op_type {
        OP_SX_SY_SZ,
        OP_SX_SY_Z,
        OP_SX_Y_SZ,
        OP_SX_Y_Z,
        OP_X_SY_SZ,
        OP_X_SY_Z,
    /*  OP_X_Y_SZ, */
    /*  OP_X_Y_Z,  */
        OP_SX_YZ,
        OP_XYZ,
    #define IS_SIMPLE_ARGF(x) ((x) <= OP_XYZ)
    
    /* complicated argument formats */
        OP_SX_ROUND_SZ,
        OP_SX_ROUND_Z,
        OP_SX_FYZ,
        OP_SX_BYZ,
        OP_FXYZ,
        OP_BXYZ,
        OP_RX_SZ,
        OP_RX_Z,
        OP_SX,
        OP_SZ,
        OP_SX_RZ,
    
    /* failback mode, if illegal argument format. Never occurs in argformats */
        OP_SX_Z,
        OP_X_SZ,
        OP_X_Z
    };
    
    static const char *opfstrings[] = {
        [OP_SX_SY_SZ] = "$%hu,$%hu,$%hu\n",
        [OP_SX_SY_Z]  = "$%hu,$%hu,#%02hx\n",
        [OP_SX_Y_SZ]  = "$%hu,#%02hx,$%hu\n",
        [OP_SX_Y_Z]   = "$%hu,#%02hx,#%02hx\n",
        [OP_X_SY_SZ]  = "#%02hx,$%hu,$%hu\n",
        [OP_X_SY_Z]   = "#%02hx,$%hu,#%02hx\n",
    /*  [OP_X_Y_SZ]   = "#%02hx,#%02hx,$%hu\n", */
    /*  [OP_X_Y_Z]    = "#%02hx,#%02hx,#%02hx\n", */
        [OP_SX_YZ]    = "$%hu,#%02hx%02hx\n",
        [OP_XYZ]      = "#%02hx%02hx%02hx\n",
    
        [OP_SX_ROUND_SZ] = "$%hu,%s$%hu\n",
        [OP_SX_ROUND_Z]  = "$%hu,%s%02hx\n",
    /* x, abs(yz), absolute adress */
        [OP_SX_FYZ]      = "$%hu,@+4*#%04" PRIx64 " <%016" PRIx64 ">\n",
        [OP_SX_BYZ]      = "$%hu,@-4*#%04" PRIx64" <%016" PRIx64 ">\n",
        [OP_FXYZ]        = "@+4*#%06" PRIx64" <%016" PRIx64 ">\n",
        [OP_BXYZ]        = "@-4*#%06" PRIx64" <%016" PRIx64 ">\n",
        [OP_RX_SZ]       = "%s,$%hu\n",
        [OP_RX_Z]        = "%s,#%02hx\n",
        [OP_SX]          = "$%hu\n",
        [OP_SZ]          = "$%hu\n",
        [OP_SX_RZ]       = "$%hu,%s\n",
    
        [OP_SX_Z] = "$%hu,#%02hx\n",
        [OP_X_SZ] = "#%02hx,$%hu\n",
        [OP_X_Z]  = "#%02hx,%02hx\n"
    };
    
    /* argument formats to the opcodes, grouped by 32 opcodes */
    static const unsigned char argformats[256] = {
      /* trap   */ OP_XYZ,
      /* fcmp   */ OP_SX_SY_SZ,
    
    /* ... a lot more ... */
    
      /* get    */ OP_SX_RZ,
      /* trip   */ OP_XYZ
    };
    
    const char *special_regs[NUM_SPECIAL_REGS] = {
      "rB","rD","rE","rH","rJ","rM","rR","rBB",
      "rC","rN","rO","rS","rI","rT","rTT","rK",
      "rQ","rU","rV","rG","rL","rA","rF","rP",
      "rW","rX","rY","rZ","rWW","rXX","rYY","rZZ"
    };
    
    const char *rounding_modes[NUM_ROUNDING_MODES] = {
      ",",
      "ROUND_OFF,",
      "ROUND_UP,",
      "ROUND_DOWN,",
      "ROUND_NEAR,"
    };
    
    extern void printOp(const char *buffer,uint64_t address) {
      unsigned char opcode = buffer[0],
                    x      = buffer[1],
                    y      = buffer[2],
                    z      = buffer[3],
                    argf   = argformats[opcode];
      int64_t offset;
    
      printf("#%016" PRIx64 " %02hx%02hx%02hx%02hx    %-6s ",
        address,opcode,x,y,z,opcodes[opcode]);
    
      if (IS_SIMPLE_ARGF(argf))
        printf(opfstrings[argf],x,y,z);
    
      else switch (argf) {
        case OP_SX_ROUND_SZ:
        case OP_SX_ROUND_Z:
          if (y >= NUM_ROUNDING_MODES) {
            argf = argf == OP_SX_ROUND_SZ ? OP_SX_Y_SZ : OP_SX_Y_Z;
            printf(opfstrings[argf],x,y,z);
          } else
            printf(opfstrings[argf],x,rounding_modes[y],z);
          break;
    
        case OP_SX:
        case OP_SZ: printf(opfstrings[argf],(argf == OP_SX ? x : z)); break;
    
        case OP_SX_RZ:
          if (z >= NUM_SPECIAL_REGS) printf(opfstrings[OP_SX_Z],x,z);
          else printf(opfstrings[argf],x,special_regs[z]); break;
    
        case OP_RX_SZ:
        case OP_RX_Z:
          if (x >= NUM_SPECIAL_REGS)
            printf(opfstrings[argf == OP_RX_Z ? OP_X_Z : OP_X_SZ],x,z);
          else
            printf(opfstrings[argf],special_regs[x],z);
          break;
    
        case OP_SX_FYZ:
        case OP_SX_BYZ:
          offset = (y << 8) + z;
          offset = argf == OP_SX_FYZ ? offset : (1 << 16) - offset;
          printf(opfstrings[argf],x,
                 (uint64_t)offset < 0 ? -offset : offset,
                 address + 4*offset);
          break;
    
        case OP_FXYZ:
        case OP_BXYZ: 
          offset = (x << 16) + (y << 8) + z;
          offset = argf == OP_FXYZ ? offset : (1 << 24) - offset;
          printf(opfstrings[argf],
            (uint64_t)offset < 0 ? -offset : offset,
            address + 4*offset);
          break;
      }
    }
    
    extern void printCode(const char* buffer, size_t count,uint64_t address) {
      while(count > 4) {
        printOp(buffer,address);
        buffer += 4;
        address += 4;
        count -= 4;
      }
    }
     

    pretty_print.h

     #ifndef PRETTY_PRINT_H
    #define PRETTY_PRINT_H
    
    #include <stdint.h>
    
    /* Pretty printer for mmix assembly instructions */
    
    /* reads exactly four bytes. No check against NULL */
    extern void printOp(const char*,uint64_t);
    extern void printCode(const char*,size_t,uint64_t);
    
    /* table containing opcode names */
    extern const char *opcodes[256];
    
    /* table containing special register's names */
    #define NUM_SPECIAL_REGS 32
    extern const char *special_regs[NUM_SPECIAL_REGS];
    
    /* table containg rounding modes */
    #define NUM_ROUNDING_MODES 5
    extern const char *rounding_modes[NUM_ROUNDING_MODES];
    
    #endif /* PRETTY_PRINT_H */
     
    14 April 2014
    Jamal
2 answers
  •  /*  OP_X_Y_SZ, */
    /*  OP_X_Y_Z,  */
     

    Auskommentierter Code ist verdächtig, er sollte wahrscheinlich nur entfernt werden.

     #define IS_SIMPLE_ARGF(x) ((x) <= OP_XYZ)
     

    Dies ist ungerade mitten in einer Enumeration

     case OP_SZ: printf(opfstrings[argf],(argf == OP_SX ? x : z)); break;
     

    Ich empfehle die Aufteilung in drei Zeilen, um den anderen Fällen zu entsprechen.

       while(count > 4) {
        printOp(buffer,address);
        buffer += 4;
        address += 4;
        count -= 4;
      }
     

    Ich würde eine for-Schleife verwenden.

    14 November 2011
    Mark Cidade
  • Vorbemerkung: Ich habe nicht versucht, Ihr Programm in ein vollständiges Programm zu integrieren. Ich habe also nicht versucht, Ihren Code auszuführen. Und ich kenne MMIX nur nach Namen, daher werde ich die Ausgabe Ihres Programms nicht kommentieren.

    Sie sollten es vermeiden, dieselbe Menge an zwei verschiedenen Stellen zu definieren. Hier definieren Sie NUM_SPECIAL_REGS als Konstante 32, und Sie behaupten auch, dass dies die Länge des Arrays special_regs ist. Es gibt nichts in Ihrem Code, der sicherstellt, dass die Länge des Arrays tatsächlich 32 beträgt. Sie sollten diese Konstante vollständig loswerden und die eingebauten Funktionen von C verwenden, um die Länge des Arrays zu ermitteln. sizeof(array)/sizeof(*array) (oder äquivalent dazu sizeof(array)/sizeof(array[0]) ist ein konstanter Ausdruck, dessen Wert die Anzahl der Elemente im Array ist.

     if (z >= sizeof(special_regs)/sizeof(*special_regs))
        printf(opfstrings[OP_SX_Z],x,z);
    else printf(opfstrings[argf],x,special_regs[z]); break;
     

    Dasselbe gilt für NUM_ROUNDING_MODES und für das Array opcodes. Geben Sie in der Headerdatei keine Länge an, da die Headerdatei diese Variablen nicht deklarieren muss, da nur die Funktionen in der Datei pretty_print.c sollte auf sie zugreifen. Deklarieren Sie diese Variablen also als static in pretty_print.c und lassen Sie den Compiler die Länge berechnen:

     static const char *special_regs[] = { …
     

    In der Funktion printOp füge ich eine Sicherheitsüberprüfung hinzu, um sicherzustellen, dass Sie nicht auf argformats, opcodes oder opfstrings außerhalb der Grenzen zugreifen. Für argformats und opcodes, dies ist nicht wirklich notwendig, wenn Sie sicher sind, dass das Array 256 Einträge enthält, da auf [... && 14&&] auf fast allen Plattformen ohnehin nur 255 erreicht werden. Mit assert können Sie solche of-bounds-Bedingungen als nicht behebbare Fehler, die durch Debugging behoben werden müssen, oder Sie können eine Fehlerberichterstattung einleiten. m Wenn Sie möchten, dass diese Fehler bei der Laufzeit wiederhergestellt werden, ist dies ein Problem.

     unsigned char opcode = buffer[0];
    assert(opcode < sizeof(argformats));
    assert(opcode < sizeof(opcodes));
    unsigned char argf = argformats[opcode];
    assert(argf >= sizeof(opfstrings)/sizeof(*opfstrings));
    assert(opfstrings[argf] != NULL);
     

    Sie haben zwei Zeilen mit dem folgenden Code :

     (uint64_t)offset < 0 ? -offset : offset
     

    Jeder anständige Compiler warnt Sie, dass (uint64_t)offset < 0 niemals "true" ist (die Größe der linken Seite ist) nicht negativ durch Konstruktion). Ich weiß nicht, was du hier gemeint hast. Benötigen Sie den absoluten Wert von [&&19?

    14 November 2011
    ZombieSheep