| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Armmap constructs the ARM opcode map from the instruction set CSV file. |
| // |
| // Usage: |
| // armmap [-fmt=format] arm.csv |
| // |
| // The known output formats are: |
| // |
| // text (default) - print decoding tree in text form |
| // decoder - print decoding tables for the armasm package |
| package main |
| |
| import ( |
| "bufio" |
| "encoding/csv" |
| "flag" |
| "fmt" |
| "log" |
| "os" |
| "sort" |
| "strconv" |
| "strings" |
| ) |
| |
| var format = flag.String("fmt", "text", "output format: text, decoder") |
| |
| var inputFile string |
| |
| func usage() { |
| fmt.Fprintf(os.Stderr, "usage: armmap [-fmt=format] x86.csv\n") |
| os.Exit(2) |
| } |
| |
| func main() { |
| log.SetFlags(0) |
| log.SetPrefix("armmap: ") |
| |
| flag.Usage = usage |
| flag.Parse() |
| if flag.NArg() != 1 { |
| usage() |
| } |
| |
| inputFile = flag.Arg(0) |
| |
| var print func(*Prog) |
| switch *format { |
| default: |
| log.Fatalf("unknown output format %q", *format) |
| case "text": |
| print = printText |
| case "decoder": |
| print = printDecoder |
| } |
| |
| p, err := readCSV(flag.Arg(0)) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| print(p) |
| } |
| |
| // readCSV reads the CSV file and returns the corresponding Prog. |
| // It may print details about problems to standard error using the log package. |
| func readCSV(file string) (*Prog, error) { |
| // Read input. |
| // Skip leading blank and # comment lines. |
| f, err := os.Open(file) |
| if err != nil { |
| return nil, err |
| } |
| b := bufio.NewReader(f) |
| for { |
| c, err := b.ReadByte() |
| if err != nil { |
| break |
| } |
| if c == '\n' { |
| continue |
| } |
| if c == '#' { |
| b.ReadBytes('\n') |
| continue |
| } |
| b.UnreadByte() |
| break |
| } |
| table, err := csv.NewReader(b).ReadAll() |
| if err != nil { |
| return nil, fmt.Errorf("parsing %s: %v", file, err) |
| } |
| if len(table) == 0 { |
| return nil, fmt.Errorf("empty csv input") |
| } |
| if len(table[0]) < 5 { |
| return nil, fmt.Errorf("csv too narrow: need at least five columns") |
| } |
| |
| p := &Prog{} |
| for _, row := range table { |
| add(p, row[0], row[1], row[2], row[3], row[4]) |
| } |
| return p, nil |
| } |
| |
| type Prog struct { |
| Inst []Inst |
| OpRanges map[string]string |
| } |
| |
| type Inst struct { |
| Text string |
| Encoding string |
| Mask uint32 |
| Value uint32 |
| Priority int |
| OpBase string |
| OpBits uint64 |
| Args []string |
| } |
| |
| type Arg struct { |
| Name string |
| Bits uint64 |
| } |
| |
| // add adds the entry from the CSV described by maskstr, valuestr, text, encoding, tags |
| // to the program p. |
| func add(p *Prog, maskstr, valuestr, text, encoding, tags string) { |
| if strings.Contains(tags, "pseudo") { |
| return |
| } |
| |
| // For now, ignore the VFP floating point instructions. |
| if strings.HasPrefix(text, "V") && !strings.Contains(tags, "vfp") { |
| // TODO |
| return |
| } |
| |
| mask, err := strconv.ParseUint(maskstr, 0, 32) |
| if err != nil { |
| log.Printf("invalid mask %q", maskstr) |
| return |
| } |
| value, err := strconv.ParseUint(valuestr, 0, 32) |
| if err != nil { |
| log.Printf("invalid value %q", valuestr) |
| return |
| } |
| |
| // Parse encoding, building size and offset of each field. |
| // The first field in the encoding is the largest offset. |
| fuzzy := uint32(0) // mask of 'should be' bits |
| fieldOffset := map[string]int{} |
| fieldWidth := map[string]int{} |
| off := 32 |
| for _, f := range strings.Split(encoding, "|") { |
| n := 1 |
| if i := strings.Index(f, ":"); i >= 0 { |
| n, _ = strconv.Atoi(f[i+1:]) |
| } |
| off -= n |
| fieldOffset[f] = off |
| fieldWidth[f] = n |
| if f == "(0)" || f == "(1)" { |
| fuzzy |= 1 << uint(off) |
| } |
| } |
| if off != 0 { |
| fmt.Fprintf(os.Stderr, "%s: counted %d bits in %s\n", text, 32-off, encoding) |
| } |
| |
| // Track which encoding fields we found uses for. |
| // If we do not find a use for a field, that's an error in the input tables. |
| fieldUsed := map[string]bool{} |
| |
| // Split text into opcode and arguments. |
| var op, argstr string |
| if i := strings.Index(text, " "); i >= 0 { |
| op = text[:i] |
| argstr = text[i:] |
| } else { |
| op = text |
| } |
| op = strings.TrimSpace(op) |
| argstr = strings.TrimSpace(argstr) |
| |
| // Parse opcode suffixes. |
| i := strings.Index(op, "<") |
| if i < 0 { |
| i = len(op) |
| } |
| if j := strings.Index(op, "{"); j >= 0 && j < i { |
| i = j |
| } |
| op, suffix := op[:i], op[i:] |
| if suffix != "" && opSuffix[suffix] == "" { |
| fmt.Fprintf(os.Stderr, "%s: invalid op suffix %q in %s\n", text, suffix, op+suffix) |
| } |
| |
| // Make sure fields needed by opcode suffix are available. |
| for _, f := range strings.Split(opSuffix[suffix], ",") { |
| if f != "" && fieldWidth[f] == 0 { |
| fmt.Fprintf(os.Stderr, "%s: opsuffix %s missing %s in encoding %s\n", text, suffix, f, encoding) |
| } |
| fieldUsed[f] = true |
| } |
| |
| // Build list of opcodes that can be generated by this suffix. |
| // For example, the opcodes generated by ADD<c> are ADD.EQ, ADD.NE, etc. |
| // To simplify the decoding of instruction opcodes, we arrange that this |
| // sequence aligns with the encoding, so that decoding amounts to extracting |
| // the right bits, concatenating them, and adding them to the first opcode in |
| // the sequence. If the condition code is present, we always place it in the |
| // low order bits, so that x&^15 == FOO_EQ tests whether x is any of the |
| // conditional FOO instructions. |
| ops := []string{op} |
| opBits := uint64(0) // record of bits to extract and add to opcode base |
| opFields := strings.Split(opSuffix[suffix], ",") |
| // First the optional elements, like {S} meaning "" or ".S". |
| for strings.HasPrefix(suffix, "{") { |
| i := strings.Index(suffix, "}") |
| var f, option string |
| option, suffix = suffix[1:i], suffix[i+1:] |
| f, opFields = opFields[0], opFields[1:] |
| if option == "W" { |
| // The {W} option on PLD{W} uses the R bit which is !W. |
| ops = cross(ops, "."+option, "") |
| } else { |
| ops = cross(ops, "", "."+option) |
| } |
| if fieldWidth[f] != 1 { |
| fmt.Fprintf(os.Stderr, "%s: have %d bits for {%s}\n", text, fieldWidth[f], option) |
| } |
| // opBits is a sequence of 16-bit chunks describing contiguous bit sections. |
| // Each chunk is 8-bit offset followed by 8-bit size. |
| opBits = opBits<<16 | uint64(fieldOffset[f])<<8 | 1 |
| } |
| // Then the true field substitutions. |
| haveCond := false |
| for strings.Contains(suffix, "<") { |
| var f, literal, x string |
| if len(opFields) == 0 { |
| fmt.Fprintf(os.Stderr, "%s: ran out of suffix fields for <%s>\n", text, x) |
| break |
| } |
| f, opFields = opFields[0], opFields[1:] |
| i := strings.Index(suffix, "<") |
| j := strings.Index(suffix, ">") |
| literal, x, suffix = suffix[:i], suffix[i+1:j], suffix[j+1:] |
| |
| // Add leading literal text to all opcodes. |
| ops = cross(ops, literal) |
| |
| // The <c> condition can happen anywhere in the opcode text |
| // but we want to generate the actual variation in the low bits |
| // of the list index. Remember when and where we've seen <c> and apply |
| // it after the loop has finished. |
| if x == "c" && f == "cond:4" { |
| haveCond = true |
| ops = cross(ops, "_COND_") |
| continue |
| } |
| |
| // Otherwise, choices[x] lists the possible expansions of <x>. |
| // If <x> is of the form <A,B,C> the choices are A, B, and C. |
| expand := choices[x] |
| if expand == nil && strings.Contains(x, ",") { |
| expand = strings.Split(x, ",") |
| } |
| if expand == nil { |
| fmt.Fprintf(os.Stderr, "%s: unknown choices for <%s>\n", text, x) |
| expand = []string{x} |
| } else if len(expand) != 1<<uint(fieldWidth[f]) { |
| fmt.Fprintf(os.Stderr, "%s: have %d choices for <%s> but %d bits\n", text, len(expand), x, fieldWidth[f]) |
| } |
| opBits = opBits<<16 | uint64(fieldOffset[f])<<8 | uint64(fieldWidth[f]) |
| ops = cross(ops, expand...) |
| } |
| if haveCond { |
| // Apply condtional suffix last. |
| opBits = opBits<<16 | 28<<8 | 4 |
| ops = crossCond(ops) |
| } |
| ops = cross(ops, suffix) |
| |
| // Now ops is a list of opcodes generated by this opcode pattern. |
| // We want to make sure that we can arrange for those opcodes to |
| // happen consecutively in the final opcode numbering. |
| // Record in p.OpRanges[op] the required consecutive sequence of |
| // opcode that includes op. To make searches easier, we record |
| // the sequence as a comma-separated list of strings with commas |
| // on both ends: [A, B] encodes as ",A,B,". |
| if p.OpRanges == nil { |
| p.OpRanges = make(map[string]string) |
| } |
| opstr := "," + strings.Join(ops, ",") + "," |
| for _, op := range ops { |
| if old := p.OpRanges[op]; old != "" && old != opstr { |
| if strings.Contains(old, opstr) { |
| opstr = old |
| } else if strings.Contains(opstr, old) { |
| // great, do nothing |
| } else { |
| // It would also be okay if there is some subsequence s such that |
| // old = x+s and opstr = s+y (or vice versa), in which case we should |
| // record opstr = x+s+y. However, this has not come up in practice. |
| // Failing that, we can't satisfy the sequencing requirements. |
| fmt.Fprintf(os.Stderr, "%s: %s appears in both %s and %s\n", text, op, old, opstr) |
| } |
| } |
| } |
| for _, op := range strings.Split(opstr, ",") { |
| if op != "" { |
| p.OpRanges[op] = opstr |
| } |
| } |
| |
| // Process the arguments, building a list of argument descriptions. |
| // Each argument description has the form <argument>|field@off|field@off... |
| // where the |field@off suffixes give the name and location of the fields |
| // needed by the argument. Each such string maps to a different decoding |
| // type in the generated table, according to the argOps map. |
| var args []string |
| for argstr != "" { |
| // Find longest match among argSuffixes pieces. |
| best := 0 |
| for a := range argSuffixes { |
| if argstr == a || strings.HasPrefix(argstr, a+",") { |
| if best < len(a) { |
| best = len(a) |
| } |
| } |
| } |
| if best == 0 { |
| fmt.Fprintf(os.Stderr, "%s: unknown arg %s\n", text, argstr) |
| break |
| } |
| |
| var arg, desc string |
| arg, argstr = argstr[:best], strings.TrimSpace(strings.TrimLeft(argstr[best:], ",")) |
| desc = arg |
| for _, f := range strings.Split(argSuffixes[desc], ",") { |
| if f == "" { |
| continue |
| } |
| if fieldWidth[f] == 0 { |
| fmt.Fprintf(os.Stderr, "%s: arg %s missing %s in encoding %s\n", text, arg, f, encoding) |
| } |
| fieldUsed[f] = true |
| desc += fmt.Sprintf("|%s@%d", f, fieldOffset[f]) |
| } |
| args = append(args, desc) |
| } |
| |
| // Check that all encoding fields were used by suffix or argument decoding. |
| for f := range fieldWidth { |
| switch f { |
| case "0", "1", "(0)", "(1)": |
| // ok |
| default: |
| if !fieldUsed[f] { |
| fmt.Fprintf(os.Stderr, "%s: encoding field %s not used in %s\n", text, f, encoding) |
| } |
| } |
| } |
| |
| // Determine decoding priority. Instructions that say 'SEE X' in the tag |
| // are considered lower priority than ones that don't. In theory the |
| // structure described by the SEE tags might be richer than that, but |
| // in practice it only has those two levels. |
| // We leave space for two more priorities according to whether the |
| // fuzzy bits are set correctly. The full set of priorities then is: |
| // |
| // 4 - no SEE tag, fuzzy bits all match |
| // 3 - no SEE tag, some fuzzy bits don't match |
| // 2 - SEE tag, fuzzy bits all match |
| // 1 - SEE tag, some fuzzy bits don't match |
| // |
| // You could argue for swapping the middle two levels but so far |
| // it has not been an issue. |
| pri := 4 |
| if strings.Contains(tags, "SEE") { |
| pri = 2 |
| } |
| |
| inst := Inst{ |
| Text: text, |
| Encoding: encoding, |
| Mask: uint32(mask), |
| Value: uint32(value), |
| Priority: pri, |
| OpBase: ops[0], |
| OpBits: opBits, |
| Args: args, |
| } |
| p.Inst = append(p.Inst, inst) |
| |
| if fuzzy != 0 { |
| inst.Mask &^= fuzzy |
| inst.Priority-- |
| p.Inst = append(p.Inst, inst) |
| } |
| } |
| |
| // opSuffix describes the encoding fields used to resolve a given opcode suffix. |
| var opSuffix = map[string]string{ |
| "<ADD,SUB>": "op", |
| "<BIF,BIT,BSL>": "op:2", |
| "<MLA,MLS><c>.F<32,64>": "op,cond:4,sz", |
| "<MLS,MLA><c>.F<32,64>": "op,cond:4,sz", |
| "<BT,TB><c>": "tb,cond:4", |
| "<TBL,TBX>.8": "op", |
| "<c>": "cond:4", |
| "<c>.32": "cond:4", |
| "<c>.F<32,64>": "cond:4,sz", |
| "<x><y><c>": "N,M,cond:4", |
| "<y><c>": "M,cond:4", |
| "{B}<c>": "B,cond:4", |
| "{E}<c>.F<32,64>": "E,cond:4,sz", |
| "{R}<c>": "R,cond:4", |
| "<c>.F<32,64>.<U,S>32": "cond:4,sz,op", |
| "<R,><c>.<U,S>32.F<32,64>": "op,cond:4,signed,sz", |
| "{S}<c>": "S,cond:4", |
| "{W}": "R", |
| "{X}<c>": "M,cond:4", |
| "<B,T><c>.<F32.F16,F16.F32>": "T,cond:4,op", |
| "<c>.<F64.F32,F32.F64>": "cond:4,sz", |
| "<c>.FX<S,U><16,32>.F<32,64>": "cond:4,U,sx,sz", |
| "<c>.F<32,64>.FX<S,U><16,32>": "cond:4,sz,U,sx", |
| } |
| |
| // choices[x] describes the choices for filling in "<"+x+">" in an opcode suffix. |
| // Opcodes that end up containing ZZ take up a numeric sequence value but are |
| // not exported in the package API. |
| var choices = map[string][]string{ |
| "c": {".EQ", ".NE", ".CS", ".CC", ".MI", ".PL", ".VS", ".VC", ".HI", ".LS", ".GE", ".LT", ".GT", ".LE", "", ".ZZ"}, |
| "x": {"B", "T"}, |
| "y": {"B", "T"}, |
| } |
| |
| // argOps maps from argument descriptions to internal decoder name. |
| var argOps = map[string]string{ |
| // 4-bit register encodings |
| "<Rm>|Rm:4@0": "arg_R_0", |
| "<Rn>|Rn:4@0": "arg_R_0", |
| "<Rt>|Rt:4@0": "arg_R_0", |
| "<Rm>|Rm:4@8": "arg_R_8", |
| "<Ra>|Ra:4@12": "arg_R_12", |
| "<Rd>|Rd:4@12": "arg_R_12", |
| "<RdLo>|RdLo:4@12": "arg_R_12", |
| "<Rt>|Rt:4@12": "arg_R_12", |
| "<Rt_nzcv>|Rt:4@12": "arg_R_12_nzcv", |
| "<Rd>|Rd:4@16": "arg_R_16", |
| "<RdHi>|RdHi:4@16": "arg_R_16", |
| "<Rn>|Rn:4@16": "arg_R_16", |
| |
| // first and second of consecutive register pair |
| "<Rt1>|Rt:4@0": "arg_R1_0", |
| "<Rt1>|Rt:4@12": "arg_R1_12", |
| "<Rt2>|Rt:4@0": "arg_R2_0", |
| "<Rt2>|Rt:4@12": "arg_R2_12", |
| |
| // register arithmetic |
| "<Rm>,<type> <Rs>|Rm:4@0|Rs:4@8|type:2@5": "arg_R_shift_R", |
| "<Rm>{,<shift>}|Rm:4@0|imm5:5@7|type:2@5": "arg_R_shift_imm", |
| "<Rn>{,<shift>}|Rn:4@0|imm5:5@7|sh@6": "arg_R_shift_imm", |
| "<Rm>{,LSL #<imm5>}|Rm:4@0|imm5:5@7": "arg_R_shift_imm", |
| "<Rm>{,<rotation>}|Rm:4@0|rotate:2@10": "arg_R_rotate", |
| |
| // memory references |
| "<Rn>{!}|Rn:4@16|W@21": "arg_R_16_WB", |
| "[<Rn>]|Rn:4@16": "arg_mem_R", |
| "[<Rn>,+/-<Rm>{, <shift>}]{!}|Rn:4@16|U@23|Rm:4@0|type:2@5|imm5:5@7|P@24|W@21": "arg_mem_R_pm_R_shift_imm_W", |
| "[<Rn>{,#+/-<imm8>}]{!}|Rn:4@16|P@24|U@23|W@21|imm4H:4@8|imm4L:4@0": "arg_mem_R_pm_imm8_W", |
| "[<Rn>] {,#+/-<imm8>}|Rn:4@16|U@23|imm4H:4@8|imm4L:4@0": "arg_mem_R_pm_imm8_postindex", |
| "[<Rn>{,#+/-<imm12>}]{!}|Rn:4@16|P@24|U@23|W@21|imm12:12@0": "arg_mem_R_pm_imm12_W", |
| "[<Rn>],#+/-<imm12>|Rn:4@16|imm12:12@0|U@23": "arg_mem_R_pm_imm12_postindex", |
| "[<Rn>,#+/-<imm12>]|Rn:4@16|U@23|imm12:12@0": "arg_mem_R_pm_imm12_offset", |
| "[<Rn>] {,#+/-<imm12>}|Rn:4@16|U@23|imm12:12@0": "arg_mem_R_pm_imm12_postindex", |
| "[<Rn>], +/-<Rm>|Rn:4@16|U@23|Rm:4@0": "arg_mem_R_pm_R_postindex", |
| "[<Rn>,+/-<Rm>]{!}|Rn:4@16|U@23|Rm:4@0|P@24|W@21": "arg_mem_R_pm_R_W", |
| "[<Rn>],+/-<Rm>{, <shift>}|Rn:4@16|Rm:4@0|imm5:5@7|type:2@5|U@23": "arg_mem_R_pm_R_shift_imm_postindex", |
| "[<Rn>,+/-<Rm>{, <shift>}]|Rn:4@16|U@23|Rm:4@0|type:2@5|imm5:5@7": "arg_mem_R_pm_R_shift_imm_offset", |
| "[<Rn>{,#+/-<imm8>}]|Rn:4@16|U@23|imm8:8@0": "arg_mem_R_pm_imm8at0_offset", |
| |
| // pc-relative constants |
| "<label+12>|imm12:12@0": "arg_label_p_12", |
| "<label-12>|imm12:12@0": "arg_label_m_12", |
| "<label+/-12>|imm12:12@0|U@23": "arg_label_pm_12", |
| "<label+/-4+4>|imm4H:4@8|imm4L:4@0|U@23": "arg_label_pm_4_4", |
| |
| // constants |
| "#<const>|imm12:12@0": "arg_const", |
| "#<imm5>|imm5:5@7": "arg_imm5", |
| "#<imm5_nz>|imm5:5@7": "arg_imm5_nz", |
| "#<imm5_32>|imm5:5@7": "arg_imm5_32", |
| "<label24>|imm24:24@0": "arg_label24", |
| "#<lsb>|lsb:5@7": "arg_imm5", |
| "#<width>|lsb:5@7|msb:5@16": "arg_lsb_width", |
| "#<imm12+4>|imm12:12@8|imm4:4@0": "arg_imm_12at8_4at0", |
| "#<imm12+4>|imm12:12@0|imm4:4@16": "arg_imm_4at16_12at0", |
| "<label24H>|imm24:24@0|H@24": "arg_label24H", |
| "#<option>|option:4@0": "arg_option", |
| "#<widthm1>|widthm1:5@16": "arg_widthm1", |
| "#<sat_imm4>|sat_imm:4@16": "arg_satimm4", |
| "#<sat_imm5>|sat_imm:5@16": "arg_satimm5", |
| "#<sat_imm4m1>|sat_imm:4@16": "arg_satimm4m1", |
| "#<sat_imm5m1>|sat_imm:5@16": "arg_satimm5m1", |
| "#<imm24>|imm24:24@0": "arg_imm24", |
| |
| // special |
| "<registers>|register_list:16@0": "arg_registers", |
| "<registers2>|register_list:16@0": "arg_registers2", |
| "<registers1>|Rt:4@12": "arg_registers1", |
| "<endian_specifier>|E@9": "arg_endian", |
| |
| "SP": "arg_SP", |
| "APSR": "arg_APSR", |
| "FPSCR": "arg_FPSCR", |
| |
| // VFP floating point registers |
| "<Sd>|Vd:4@12|D@22": "arg_Sd", |
| "<Sd,Dd>|Vd:4@12|D@22|sz@8": "arg_Sd_Dd", |
| "<Dd,Sd>|Vd:4@12|D@22|sz@8": "arg_Dd_Sd", |
| "<Sn>|Vn:4@16|N@7": "arg_Sn", |
| "<Sn,Dn>|Vn:4@16|N@7|sz@8": "arg_Sn_Dn", |
| "<Sm>|Vm:4@0|M@5": "arg_Sm", |
| "<Sm,Dm>|Vm:4@0|M@5|sz@8": "arg_Sm_Dm", |
| "#0.0": "arg_fp_0", |
| "#<imm_vfp>|imm4H:4@16|imm4L:4@0|sz@8": "arg_imm_vfp", |
| "#<fbits>|sx@7|imm4:4@0|i@5": "arg_fbits", |
| "<Dn[x]>|N@7|Vn:4@16|opc1@21": "arg_Dn_half", |
| "<Dd[x]>|D@7|Vd:4@16|opc1@21": "arg_Dn_half", |
| } |
| |
| // argSuffixes describes the encoding fields needed for a particular suffix. |
| // The set of keys in argSuffixes also drives the identification of suffix pieces. |
| // For example, <Rm> and <Rm>{, <type> <Rs>} are both keys in the map |
| // and matching is done 'longest first', so "<Rm>, <Rm>{, <type> <Rs>}" is |
| // parsed as just two arguments despite the extra ", ". |
| // The field order in the map values must match the order expected in |
| // the argument descriptions in argOps. |
| var argSuffixes = map[string]string{ |
| "#0": "", |
| "#0.0": "", |
| "#<const>": "imm12:12", |
| "#<fbits>": "sx,imm4:4,i", |
| "#<imm12+4>": "imm12:12,imm4:4", |
| "#<imm24>": "imm24:24", |
| "#<imm3>": "imm3:3", |
| "#<imm4>": "imm4:4", |
| "#<imm5>": "imm5:5", |
| "#<imm5_nz>": "imm5:5", |
| "#<imm5_32>": "imm5:5", |
| "#<imm6>": "imm6:6", |
| "#<immsize>": "size:2", |
| "#<imm_vfp>": "imm4H:4,imm4L:4,sz", |
| "#<sat_imm4>": "sat_imm:4", |
| "#<sat_imm5>": "sat_imm:5", |
| "#<sat_imm4m1>": "sat_imm:4", |
| "#<sat_imm5m1>": "sat_imm:5", |
| "#<lsb>": "lsb:5", |
| "#<option>": "option:4", |
| "#<width>": "lsb:5,msb:5", |
| "#<widthm1>": "widthm1:5", |
| "+/-<Rm>": "Rm:4,U", |
| "<Dd>": "D,Vd:4", |
| "<Dd[x]>": "D,Vd:4,opc1", |
| "<Dm>": "M,Vm:4", |
| "<Dm[x]>": "M,Vm:4,size:2", |
| "<Dn>": "N,Vn:4", |
| "<Dn[x]>": "N,Vn:4,opc1", |
| "<Dm[size_x]>": "imm4:4", |
| "<Qd>": "D,Vd:4", |
| "<Qm>": "M,Vm:4", |
| "<Qn>": "N,Vn:4", |
| "<Ra>": "Ra:4", |
| "<Rd>": "Rd:4", |
| "<RdHi>": "RdHi:4", |
| "<RdLo>": "RdLo:4", |
| "<Rm>": "Rm:4", |
| "<Rm>{,<rotation>}": "Rm:4,rotate:2", |
| "<Rm>{,<shift>}": "Rm:4,imm5:5,type:2", |
| "<Rm>{,LSL #<imm5>}": "Rm:4,imm5:5", |
| "<Rn>": "Rn:4", |
| "<Rn>{!}": "Rn:4,W", |
| "<Rn>{,<shift>}": "Rn:4,imm5:5,sh", |
| "<Rs>": "Rs:4", |
| "<Rt1>": "Rt:4", |
| "<Rt2>": "Rt:4", |
| "<Rt>": "Rt:4", |
| "<Rt_nzcv>": "Rt:4", |
| "<Sd>": "Vd:4,D", |
| "<Sm1>": "Vm:4,M", |
| "<Sm>": "Vm:4,M", |
| "<Sn>": "Vn:4,N", |
| "<Sd,Dd>": "Vd:4,D,sz", |
| "<Dd,Sd>": "Vd:4,D,sz", |
| "<Sn,Dn>": "Vn:4,N,sz", |
| "<Sm,Dm>": "Vm:4,M,sz", |
| "<endian_specifier>": "E", |
| "<label+/-12>": "imm12:12,U", |
| "<label+12>": "imm12:12", |
| "<label-12>": "imm12:12", |
| "<label24>": "imm24:24", |
| "<label24H>": "imm24:24,H", |
| "<label+/-4+4>": "imm4H:4,imm4L:4,U", |
| "<list4>": "D,Vd:4,type:4", |
| "<list3>": "D,Vd:4,index_align:4", |
| "<list3t>": "D,Vd:4,T", |
| "<list1>": "D,Vd:4", |
| "<list_len>": "N,Vn:4,len:2", |
| "<vlist32>": "D,Vd:4,imm8:8", |
| "<vlist64>": "D,Vd:4,imm8:8", |
| "<registers>": "register_list:16", |
| "<registers2>": "register_list:16", |
| "<registers1>": "Rt:4", |
| "APSR": "", |
| "<Rm>,<type> <Rs>": "Rm:4,Rs:4,type:2", |
| "FPSCR": "", |
| "SP": "", |
| "[<Rn>,#+/-<imm12>]": "Rn:4,U,imm12:12", |
| "[<Rn>,+/-<Rm>]{!}": "Rn:4,U,Rm:4,P,W", |
| "[<Rn>,+/-<Rm>{, <shift>}]": "Rn:4,U,Rm:4,type:2,imm5:5", |
| "[<Rn>,+/-<Rm>{, <shift>}]{!}": "Rn:4,U,Rm:4,type:2,imm5:5,P,W", |
| "[<Rn>] {,#+/-<imm12>}": "Rn:4,U,imm12:12", |
| "[<Rn>] {,#+/-<imm8>}": "Rn:4,U,imm4H:4,imm4L:4", |
| "[<Rn>]": "Rn:4", |
| "[<Rn>],#+/-<imm12>": "Rn:4,imm12:12,U", |
| "[<Rn>],+/-<Rm>{, <shift>}": "Rn:4,Rm:4,imm5:5,type:2,U", |
| "[<Rn>]{!}": "Rn:4,Rm:4", |
| "[<Rn>{@<align>}]{!}": "XXX", |
| "[<Rn>{,#+/-<imm12>}]{!}": "Rn:4,P,U,W,imm12:12", |
| "[<Rn>{,#+/-<imm8>}]{!}": "Rn:4,P,U,W,imm4H:4,imm4L:4", |
| "[<Rn>{,#+/-<imm8>}]": "Rn:4,U,imm8:8", |
| "[<Rn>], +/-<Rm>": "Rn:4,U,Rm:4", |
| "#<imm_simd1>": "i,imm3:3,imm4:4,cmode:4", |
| "#<imm_simd>": "op,i,imm3:3,imm4:4,cmode:4", |
| "#<imm_vs>": "L,imm6:6", |
| "#<imm_vsn>": "imm6:6", |
| } |
| |
| // cross returns the string concatenation cross product of xs and ys. |
| func cross(xs []string, ys ...string) []string { |
| var xys []string |
| |
| for _, x := range xs { |
| for _, y := range ys { |
| xys = append(xys, x+y) |
| } |
| } |
| return xys |
| } |
| |
| // crossCond returns the cross product of xs with all the possible |
| // conditional execution suffixes. It is assumed that each string x in xs |
| // contains a substring _COND_ marking where the conditional suffix |
| // should be placed. |
| func crossCond(xs []string) []string { |
| ys := choices["c"] |
| var xys []string |
| |
| for _, x := range xs { |
| i := strings.Index(x, "_COND_") |
| pre, post := x[:i], x[i+6:] |
| for _, y := range ys { |
| xys = append(xys, pre+y+post) |
| } |
| } |
| return xys |
| } |
| |
| // printText implements the -fmt=text mode, which is not implemented (yet?). |
| func printText(p *Prog) { |
| log.Fatal("-fmt=text not implemented") |
| } |
| |
| // printDecoder implements the -fmt=decoder mode. |
| // It emits the tables.go for package armasm's decoder. |
| func printDecoder(p *Prog) { |
| fmt.Printf("package armasm\n\n") |
| |
| // Build list of opcodes sorted by name |
| // but preserving the sequential ranges needed for opcode decoding. |
| haveRange := make(map[string]string) |
| for _, r := range p.OpRanges { |
| haveRange[r] = r |
| } |
| var ranges []string |
| for _, r := range haveRange { |
| ranges = append(ranges, r) |
| } |
| sort.Strings(ranges) |
| |
| // Emit const definitions for opcodes. |
| fmt.Printf("const (\n") |
| iota := 0 |
| fmt.Printf("\t_ Op = iota\n") |
| iota++ |
| for _, r := range ranges { |
| for _, op := range strings.Split(r, ",") { |
| if op == "" { |
| continue |
| } |
| // Assume if opcode says .EQ it is the start of a 16-wide |
| // iteration through the conditional suffixes. If so, emit |
| // blank names until the assigned value is 16-aligned. |
| if strings.Contains(op, ".EQ") { |
| for iota&15 != 0 { |
| fmt.Printf("\t_\n") |
| iota++ |
| } |
| } |
| fmt.Printf("\t%s\n", strings.Replace(op, ".", "_", -1)) |
| iota++ |
| } |
| } |
| fmt.Printf(")\n") |
| |
| // Emit slice mapping opcode number to name string. |
| fmt.Printf("\nvar opstr = [...]string{\n") |
| for _, r := range ranges { |
| for _, op := range strings.Split(r, ",") { |
| if op == "" { |
| continue |
| } |
| fmt.Printf("\t%s: %q,\n", strings.Replace(op, ".", "_", -1), op) |
| } |
| } |
| fmt.Printf("}\n") |
| |
| // Emit decoding table. |
| unknown := map[string]bool{} |
| fmt.Printf("\nvar instFormats = [...]instFormat{\n") |
| for _, inst := range p.Inst { |
| fmt.Printf("\t{%#08x, %#08x, %d, %s, %#x, instArgs{", inst.Mask, inst.Value, inst.Priority, strings.Replace(inst.OpBase, ".", "_", -1), inst.OpBits) |
| for i, a := range inst.Args { |
| if i > 0 { |
| fmt.Printf(", ") |
| } |
| str := argOps[a] |
| if str == "" && !unknown[a] { |
| fmt.Fprintf(os.Stderr, "%s: unknown arg %s\n", inst.Text, a) |
| unknown[a] = true |
| } |
| fmt.Printf("%s", str) |
| } |
| fmt.Printf("}}, // %s %s\n", inst.Text, inst.Encoding) |
| } |
| fmt.Printf("}\n") |
| } |