// 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")
}
