// Copyright 2024 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.

// S390xspec reads the Principles of Operation PDF Manual
// to collect instruction encoding details and writes those details to standard output
// in CSV format.
//
// Usage:
//
//	s390xspec z_Architecture_Principles_of_Operation.pdf > s390x.csv
//
// Each CSV line contains three fields:
//
//	instruction
//		The instruction heading, such as "BRANCH AND LINK".
//	mnemonic
//		The instruction mnemonics, such as "BAL R1,D2(X2,B2)".
//	encoding
//		The instruction encoding, a sequence of opcode and operands encoding in respective bit positions
//		such as operand@bitposition each separated by |
//		Ex: "45@0|R1@8|X2@12|B2@16|D2@20|"
//
// For more on the exact meaning of these fields, see the Principle of Operations IBM-Z Architecture PDF Manual.
package main

import (
	"bufio"
	"fmt"
	"log"
	"math"
	"os"
	"sort"
	"strconv"
	"strings"

	"rsc.io/pdf"
)

type Inst struct {
	Name  string
	Text  string
	Enc   string
	Flags string
}

var stdout *bufio.Writer

func main() {
	log.SetFlags(0)
	log.SetPrefix("s390xspec: ")

	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, "usage: s390xspec file.pdf\n")
		os.Exit(2)
	}

	f, err := pdf.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}

	// Split across multiple columns and pages!
	var all = []Inst{}

	// Scan document looking for instructions.
	// Must find exactly the ones in the outline.
	n := f.NumPage()
	for pageNum := 1; pageNum <= n; pageNum++ {
		page := f.Page(pageNum)
		t1 := getPageContent(page)
		if len(t1) > 0 && match(t1[0], "Helvetica-Bold", 13.98, "Instructions Arranged by Name") {
			for n := pageNum; n < pageNum+24; n++ {
				page := f.Page(n)
				table := parsePage(n, page)
				all = append(all, table...)
			}
			break
		} else {
			continue
		}
	}
	stdout = bufio.NewWriter(os.Stdout)
	for _, inst := range all {
		if strings.Contains(inst.Name, "\x00I") {
			r := rune(0x2190)
			inst.Name = strings.Replace(inst.Name, "\x00I", string(r), -1)
		} else if strings.Contains(inst.Name, "I\x00") {
			r := rune(0x2192)
			inst.Name = strings.Replace(inst.Name, "I\x00", string(r), -1)
		}
		fmt.Fprintf(stdout, "%q,%q,%q,%q\n", inst.Name, inst.Text, inst.Enc, inst.Flags)
	}
	stdout.Flush()

}

// getPageContent gets the page content of a single PDF page
func getPageContent(p pdf.Page) []pdf.Text {
	var text []pdf.Text

	content := p.Content()
	for _, t := range content.Text {
		text = append(text, t)
	}

	text = findWords(text)
	return text
}

// parsePage parses single PDF page and returns the instructions content
func parsePage(num int, p pdf.Page) []Inst {
	var insts []Inst
	text := getPageContent(p)

	for {
		var heading, mnemonic, format string
		// The float numbers below are the horizontal X-coordinate values to be parsed out of the Z-ISA PDF book.
		for len(text) > 0 && !(match(text[0], "Helvetica-Narrow", 8, "") && (matchXCord(text[0], 73.9) || matchXCord(text[0], 55.9))) {
			text = text[1:]
		}
		if len(text) == 0 {
			break
		}
		heading = text[0].S
		text = text[1:]
		// The float numbers below are the horizontal X-coordinate values to be parsed out of the Z-ISA PDF book.
		for !(matchXCord(text[0], 212.2) || matchXCord(text[0], 230.1) || matchXCord(text[0], 246.2) || matchXCord(text[0], 264.2)) {
			heading += text[0].S
			if match(text[0], "Wingdings3", 0, "") {
				heading += text[1].S
				text = text[1:]
			}
			text = text[1:]
		}
		if strings.Compare(heading, "DIAGNOSE") == 0 {
			text = text[1:]
			continue
		}
		heading, check, m := checkHeading(heading)
		if check {
			mnemonic = m
		} else {
			mnemonic = text[0].S
			text = text[1:]
		}
		index := strings.Index(mnemonic, " ")
		if index != -1 {
			format = mnemonic[index+1:]
			mnemonic = mnemonic[:index]
		} else {
			format = text[0].S
		}
		text = text[1:]
		if strings.Compare(format, "SS") == 0 {
			format += text[0].S
		}
		before, _, _ := strings.Cut(format, " ")
		format = before
		// The float numbers below are the horizontal X-coordinate values to be parsed out of the Z-ISA PDF book.
		for len(text) > 0 && !(match(text[0], "Helvetica-Narrow", 8, "") && (matchXCord(text[0], 350.82) || matchXCord(text[0], 363.84) || matchXCord(text[0], 332.82) || matchXCord(text[0], 345.84))) {
			if text[0].X > 405.48 {
				break
			}
			text = text[1:]
		}
		flags := text[0].S
		// The float numbers below are the horizontal X-coordinate values to be parsed out of the Z-ISA PDF book.
		for len(text) > 0 && !(match(text[0], "Helvetica-Narrow", 8, "") && ((matchXCord(text[0], 481.7) && (!matchXCord(text[1], 496.1))) || matchXCord(text[0], 496.1) || (matchXCord(text[0], 499.6) && (!matchXCord(text[1], 514))) || (matchXCord(text[0], 514)))) {
			text = text[1:]
		}
		if len(text) == 0 {
			break
		}
		opcode := text[0].S
		b1, b2, _ := strings.Cut(opcode, " ")
		if matchXCord(text[0], 481.7) || matchXCord(text[0], 499.6) {
			opcode = b2
		} else {
			opcode = b1
		}
		if strings.Compare(text[0].S, b1) == 0 {
			text = text[2:]
		} else {
			text = text[1:]
		}
		mnemonic1, encoding := frameMnemonic(mnemonic, format, opcode)
		for match(text[0], "Helvetica-Narrow", 5.1, "") {
			text = text[1:]
		}
		if match(text[0], "Helvetica-Oblique", 9, "") {
			text = text[2:]
			insts = append(insts, Inst{heading, mnemonic1, encoding, flags})
			continue
		}
		if strings.HasPrefix(text[0].S, "(") {
			y123 := text[0].Y
			for text[0].Y == y123 && !matchXCord(text[0], 5.1) {
				heading += text[0].S
				text = text[1:]
			}
		} else if !(math.Abs(text[0].Y-text[1].Y) < 0.3) {
			heading += " " + text[0].S
			text = text[1:]
		}
		insts = append(insts, Inst{heading, mnemonic1, encoding, flags})
		if match(text[0], "Helvetica-Oblique", 9, "") {
			break
		}
	}
	return insts
}

func checkHeading(heading string) (string, bool, string) {
	substr := []string{"ALSI", "ALGSI", "CHRL", "CGHRL", "CUXTR", "IEXTR", "RXSBG", "RISBLG", "VERIM", "VPSOP"}
	b := false
	for _, s := range substr {
		r1 := strings.Index(heading, s)
		if r1 != -1 {
			heading = heading[:r1-1]
			b = true
			return heading, b, s
		}
	}
	return heading, b, ""
}

func frameMnemonic(mnemonic, format, opcode string) (string, string) {

	var mn, enc string

	switch format {
	case "E":
		mn, enc = mnemonic_E(mnemonic, opcode)
	case "I":
		mn, enc = mnemonic_I(mnemonic, opcode)
	case "IE":
		mn, enc = mnemonic_IE(mnemonic, opcode)
	case "MII":
		mn, enc = mnemonic_MII(mnemonic, opcode)
	case "RI-a", "RI-b", "RI-c":
		mn, enc = mnemonic_RI(mnemonic, format, opcode)
	case "RIE-a", "RIE-b", "RIE-c", "RIE-d", "RIE-e", "RIE-f", "RIE-g":
		mn, enc = mnemonic_RIE(mnemonic, format, opcode)
	case "RIL-a", "RIL-b", "RIL-c":
		mn, enc = mnemonic_RIL(mnemonic, format, opcode)
	case "RIS":
		mn, enc = mnemonic_RIS(mnemonic, opcode)
	case "RR":
		mn, enc = mnemonic_RR(mnemonic, opcode)
	case "RRD":
		mn, enc = mnemonic_RRD(mnemonic, opcode)
	case "RRE":
		mn, enc = mnemonic_RRE(mnemonic, opcode)
	case "RRF-a", "RRF-b", "RRF-c", "RRF-d", "RRF-e":
		mn, enc = mnemonic_RRF(mnemonic, format, opcode)
	case "RRS":
		mn, enc = mnemonic_RRS(mnemonic, opcode)
	case "RS-a", "RS-b":
		mn, enc = mnemonic_RS(mnemonic, format, opcode)
	case "RSI":
		mn, enc = mnemonic_RSI(mnemonic, opcode)
	case "RSL-a", "RSL-b":
		mn, enc = mnemonic_RSL(mnemonic, format, opcode)
	case "RSY-a", "RSY-b":
		mn, enc = mnemonic_RSY(mnemonic, format, opcode)
	case "RX-a", "RX-b":
		mn, enc = mnemonic_RX(mnemonic, format, opcode)
	case "RXE":
		mn, enc = mnemonic_RXE(mnemonic, opcode)
	case "RXF":
		mn, enc = mnemonic_RXF(mnemonic, opcode)
	case "RXY-a", "RXY-b":
		mn, enc = mnemonic_RXY(mnemonic, format, opcode)
	case "S":
		mn, enc = mnemonic_S(mnemonic, opcode)
	case "SI":
		mn, enc = mnemonic_SI(mnemonic, opcode)
	case "SIL":
		mn, enc = mnemonic_SIL(mnemonic, opcode)
	case "SIY":
		mn, enc = mnemonic_SIY(mnemonic, opcode)
	case "SMI":
		mn, enc = mnemonic_SMI(mnemonic, opcode)
	case "SS-a", "SS-b", "SS-c", "SS-d", "SS-e", "SS-f":
		mn, enc = mnemonic_SS(mnemonic, format, opcode)
	case "SSE":
		mn, enc = mnemonic_SSE(mnemonic, opcode)
	case "SSF":
		mn, enc = mnemonic_SSF(mnemonic, opcode)
	case "VRI-a", "VRI-b", "VRI-c", "VRI-d", "VRI-e", "VRI-f", "VRI-g", "VRI-h", "VRI-i":
		mn, enc = mnemonic_VRI(mnemonic, format, opcode)
	case "VRR-a", "VRR-b", "VRR-c", "VRR-d", "VRR-e", "VRR-f", "VRR-g", "VRR-h", "VRR-i", "VRR-j", "VRR-k":
		mn, enc = mnemonic_VRR(mnemonic, format, opcode)
	case "VRS-a", "VRS-b", "VRS-c", "VRS-d":
		mn, enc = mnemonic_VRS(mnemonic, format, opcode)
	case "VRV":
		mn, enc = mnemonic_VRV(mnemonic, opcode)
	case "VRX":
		mn, enc = mnemonic_VRX(mnemonic, opcode)
	case "VSI":
		mn, enc = mnemonic_VSI(mnemonic, opcode)
	default:
		mn = mnemonic
	}
	return mn, enc
}

func mnemonic_E(mnemonic, opcode string) (string, string) {
	var enc string
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	enc = str + "@0|??@16"
	return mnemonic, enc
}

func mnemonic_I(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " I"
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	enc = str + "@0|I@8|??@16"
	return mnemonic, enc
}

func mnemonic_IE(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " I1,I2"
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	enc = str + "@0|//@16|I1@24|I2@28|??@32"
	return mnemonic, enc
}

func mnemonic_MII(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " M1,RI2,RI3"
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	enc = str + "@0|M1@8|RI2@12|RI3@24|??@48"
	return mnemonic, enc
}

func mnemonic_RI(mnemonic, format, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:3], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch format {
	case "RI-a":
		mnemonic += " R1,I2"
		enc = str1 + "@0|R1@8|" + str2 + "@12|I2@16|??@32"
	case "RI-b":
		mnemonic += " R1,RI2"
		enc = str1 + "@0|R1@8|" + str2 + "@12|RI2@16|??@32"
	case "RI-c":
		mnemonic += " M1,RI2"
		enc = str1 + "@0|M1@8|" + str2 + "@12|RI2@16|??@32"
	}
	return mnemonic, enc
}

func mnemonic_RIE(mnemonic, format, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch format {
	case "RIE-a":
		mnemonic += " R1,I2,M3"
		enc = str1 + "@0|R1@8|//@12|I2@16|M3@32|//@36|" + str2 + "@40|??@48"
	case "RIE-b":
		mnemonic += " R1,R2,M3,RI4"
		enc = str1 + "@0|R1@8|R2@12|RI4@16|M3@32|//@36|" + str2 + "@40|??@48"
	case "RIE-c":
		mnemonic += " R1,I2,M3,RI4"
		enc = str1 + "@0|R1@8|M3@12|RI4@16|I2@32|" + str2 + "@40|??@48"
	case "RIE-d":
		mnemonic += " R1,R3,I2"
		enc = str1 + "@0|R1@8|R3@12|I2@16|//@32|" + str2 + "@40|??@48"
	case "RIE-e":
		mnemonic += " R1,R3,RI2"
		enc = str1 + "@0|R1@8|R3@12|RI2@16|//@32|" + str2 + "@40|??@48"
	case "RIE-f":
		mnemonic += " R1,R2,I3,I4,I5"
		enc = str1 + "@0|R1@8|R2@12|I3@16|I4@24|I5@32|" + str2 + "@40|??@48"
	case "RIE-g":
		mnemonic += " R1,I2,M3"
		enc = str1 + "@0|R1@8|M3@12|I2@16|//@32|" + str2 + "@40|??@48"
	}
	return mnemonic, enc
}

func mnemonic_RIL(mnemonic, format, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch format {
	case "RIL-a":
		mnemonic += " R1,I2"
		enc = str1 + "@0|R1@8|" + str2 + "@12|I2@16|??@48"
	case "RIL-b":
		mnemonic += " R1,RI2"
		enc = str1 + "@0|R1@8|" + str2 + "@12|RI2@16|??@48"
	case "RIL-c":
		mnemonic += " M1,RI2"
		enc = str1 + "@0|M1@8|" + str2 + "@12|RI2@16|??@48"
	}
	return mnemonic, enc
}

func mnemonic_RIS(mnemonic, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	mnemonic += " R1,I2,M3,D4(B4)"
	enc = str1 + "@0|R1@8|M3@12|B4@16|D4@20|I2@32|" + str2 + "@40|??@48"
	return mnemonic, enc
}

func mnemonic_RR(mnemonic, opcode string) (string, string) {
	var enc string
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	switch mnemonic {
	case "BCR":
		mnemonic += " M1,R2"
		enc = str + "@0|M1@8|R2@12|??@16"
	case "SPM":
		mnemonic += " R1"
		enc = str + "@0|R1@8|//@12|??@16"
	default:
		mnemonic += " R1,R2"
		enc = str + "@0|R1@8|R2@12|??@16"
	}
	return mnemonic, enc
}

func mnemonic_RRD(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " R1,R3,R2"
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	enc = str + "@0|R1@16|//@20|R3@24|R2@28|??@32"
	return mnemonic, enc
}

func mnemonic_RRE(mnemonic, opcode string) (string, string) {
	var enc string
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	switch mnemonic {
	case "LZER", "LZDR", "LZXR", "EFPC", "EPAR", "EPAIR", "ESEA", "ESAIR", "ESAR", "ETND", "IAC", "IPM", "MSTA", "PTF", "SFASR", "SFPC", "SSAR", "SSAIR":
		mnemonic += " R1"
		enc = str + "@0|//@16|R1@24|//@28|??@32"
	case "NNPA", "PALB", "PCC", "PCKMO":
		enc = str + "@0|//@16|??@32"
	default:
		mnemonic += " R1,R2"
		enc = str + "@0|//@16|R1@24|R2@28|??@32"
	}
	return mnemonic, enc
}

func mnemonic_RRF(mnemonic, format, opcode string) (string, string) {
	var enc string
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	switch format {
	case "RRF-a":
		switch mnemonic {
		case "SELR", "SELGR", "SELFHR", "IPTE", "AXTRA", "ADTRA",
			"DDTRA", "DXTRA", "MDTRA", "MXTRA", "SDTRA", "SXTRA":
			mnemonic += " R1,R2,R3,M4"
			enc = str + "@0|R3@16|M4@20|R1@24|R2@28|??@32"
		default:
			mnemonic += " R1,R2,R3"
			enc = str + "@0|R3@16|//@20|R1@24|R2@28|??@32"
		}
	case "RRF-b":
		switch mnemonic {
		case "CRDTE", "IDTE", "LPTEA", "RDP", "DIEBR", "DIDBR",
			"QADTR", "QAXTR", "RRDTR", "RRXTR":
			mnemonic += " R1,R3,R2,M4"
			enc = str + "@0|R3@16|M4@20|R1@24|R2@28|??@32"
		default:
			mnemonic += " R1,R3,R2"
			enc = str + "@0|R3@16|//@20|R1@24|R2@28|??@32"
		}
	case "RRF-c":
		mnemonic += " R1,R2,M3"
		enc = str + "@0|M3@16|//@20|R1@24|R2@28|??@32"
	case "RRF-d":
		mnemonic += " R1,R2,M4"
		enc = str + "@0|//@16|M4@20|R1@24|R2@28|??@32"
	case "RRF-e":
		switch mnemonic {
		case "CXFBRA", "CXFTR", "CDFBRA", "CDFTR", "CEFBRA", "CXGBRA", "CXGTRA", "CDGBRA", "CDGTRA", "CEGBRA", "CXLFBR", "CXLFTR", "CDLFBR", "CDLFTR", "CELFBR",
			"CXLGBR", "CXLGTR", "CDLGBR", "CDLGTR", "CELGBR", "CFXBRA", "CGXBRA", "CFXTR", "CGXTRA", "CFDBRA", "CGDBRA", "CFDTR", "CGDTRA", "CFEBRA", "CGEBRA",
			"CLFEBR", "CLFDBR", "CLFXBR", "CLGEBR", "CLGDBR", "CLGXBR", "CLFXTR", "CLFDTR", "CLGXTR", "CLGDTR", "FIEBRA", "FIDBRA", "FIXBRA", "FIDTR", "FIXTR",
			"LDXBRA", "LEDBRA", "LEXBRA", "LEDTR", "LDXTR":
			mnemonic += " R1,M3,R2,M4"
			enc = str + "@0|M3@16|M4@20|R1@24|R2@28|??@32"
		default:
			mnemonic += " R1,M3,R2"
			enc = str + "@0|M3@16|//@20|R1@24|R2@28|??@32"
		}
	}
	return mnemonic, enc
}

func mnemonic_RRS(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " R1,R2,M3,D4(B4)"
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	enc = str1 + "@0|R1@8|R2@12|B4@16|D4@20|M3@32|//@36|" + str2 + "@40|??@48"
	return mnemonic, enc
}

func mnemonic_RS(mnemonic, format, opcode string) (string, string) {
	var enc string
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	switch format {
	case "RS-a":
		switch mnemonic {
		case "SLDA", "SLDL", "SLA", "SLL", "SRA", "SRDA", "SRDL", "SRL":
			mnemonic += " R1,D2(B2)"
			enc = str + "@0|R1@8|//@12|B2@16|D2@20|??@32"
		default:
			mnemonic += " R1,R3,D2(B2)"
			enc = str + "@0|R1@8|R3@12|B2@16|D2@20|??@32"
		}
	case "RS-b":
		mnemonic += " R1,M3,D2(B2)"
		enc = str + "@0|R1@8|M3@12|B2@16|D2@20|??@32"
	}
	return mnemonic, enc
}

func mnemonic_RSI(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " R1,R3,RI2"
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	enc = str + "@0|R1@8|R3@12|RI2@16|??@32"
	return mnemonic, enc
}

func mnemonic_RSL(mnemonic, format, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch format {
	case "RSL-a":
		mnemonic += " D1(L1,B1)"
		enc = str1 + "@0|L1@8|//@12|B1@16|D1@20|//@32|" + str2 + "@40|??@48"
	case "RSL-b":
		mnemonic += " R1,D2(L2,B2),M3"
		enc = str1 + "@0|L2@8|B2@16|D2@20|R1@32|M3@36|" + str2 + "@40|??@48"
	}
	return mnemonic, enc
}

func mnemonic_RSY(mnemonic, format, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch format {
	case "RSY-a":
		mnemonic += " R1,R3,D2(B2)"
		enc = str1 + "@0|R1@8|R3@12|B2@16|D2@20|" + str2 + "@40|??@48"
	case "RSY-b":
		switch mnemonic {
		case "LOC", "LOCFH", "LOCG", "STOCFH", "STOC", "STOCG":
			mnemonic += " R1,D2(B2),M3"
		default:
			mnemonic += " R1,M3,D2(B2)"
		}
		enc = str1 + "@0|R1@8|M3@12|B2@16|D2@20|" + str2 + "@40|??@48"
	}
	return mnemonic, enc
}

func mnemonic_RX(mnemonic, format, opcode string) (string, string) {
	var enc string
	val, _ := strconv.ParseInt(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	switch format {
	case "RX-a":
		mnemonic += " R1,D2(X2,B2)"
		enc = str + "@0|R1@8|X2@12|B2@16|D2@20|??@32"
	case "RX-b":
		mnemonic += " M1,D2(X2,B2)"
		enc = str + "@0|M1@8|X2@12|B2@16|D2@20|??@32"
	}
	return mnemonic, enc
}

func mnemonic_RXE(mnemonic, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch mnemonic {
	case "LCBB":
		mnemonic += " R1,D2(X2,B2),M3"
		enc = str1 + "@0|R1@8|X2@12|B2@16|D2@20|M3@32|//@36|" + str2 + "@40|??@48"
	default:
		mnemonic += " R1,D2(X2,B2)"
		enc = str1 + "@0|R1@8|X2@12|B2@16|D2@20|//@32|" + str2 + "@40|??@48"
	}
	return mnemonic, enc
}

func mnemonic_RXF(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " R1,R3,D2(X2,B2)"
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	enc = str1 + "@0|R3@8|X2@12|B2@16|D2@20|R1@32|//@36|" + str2 + "@40|??@48"
	return mnemonic, enc
}

func mnemonic_RXY(mnemonic, format, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch format {
	case "RXY-a":
		mnemonic += " R1,D2(X2,B2)"
		enc = str1 + "@0|R1@8|X2@12|B2@16|D2@20|" + str2 + "@40|??@48"
	case "RXY-b":
		mnemonic += " M1,D2(X2,B2)"
		enc = str1 + "@0|M1@8|X2@12|B2@16|D2@20|" + str2 + "@40|??@48"
	}
	return mnemonic, enc
}

func mnemonic_S(mnemonic, opcode string) (string, string) {
	var enc string
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	switch mnemonic {
	case "PTLB", "TEND", "XSCH", "CSCH", "HSCH", "IPK", "RCHP", "RSCH", "SAL", "SCHM":
		enc = str + "@0|//@16|??@32"
	default:
		mnemonic += " D2(B2)"
		enc = str + "@0|B2@16|D2@20|??@32"
	}
	return mnemonic, enc
}

func mnemonic_SI(mnemonic, opcode string) (string, string) {
	var enc string
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	switch mnemonic {
	case "TS", "SSM", "LPSW":
		mnemonic += " D1(B1)"
	default:
		mnemonic += " D1(B1),I2"
	}
	enc = str + "@0|I2@8|B1@16|D1@20|??@32"
	return mnemonic, enc
}

func mnemonic_SIL(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " D1(B1),I2"
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	enc = str + "@0|B1@16|D1@20|I2@32|??@48"
	return mnemonic, enc
}

func mnemonic_SIY(mnemonic, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch mnemonic {
	case "LPSWEY":
		mnemonic += " D1(B1)"
		enc = str1 + "@0|//@8|B1@16|D1@20|" + str2 + "@40|??@48"
	default:
		mnemonic += " D1(B1),I2"
		enc = str1 + "@0|I2@8|B1@16|D1@20|" + str2 + "@40|??@48"
	}
	return mnemonic, enc
}

func mnemonic_SMI(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " M1,RI2,D3(B3)"
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	enc = str + "@0|M1@8|//@12|B3@16|D3@20|RI2@32|??@48"
	return mnemonic, enc
}

func mnemonic_SS(mnemonic, format, opcode string) (string, string) {
	var enc string
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	switch format {
	case "SS-a":
		mnemonic += " D1(L1,B1),D2(B2)"
		enc = str + "@0|L1@8|B1@16|D1@20|B2@32|D2@36|??@48"
	case "SS-b":
		mnemonic += " D1(L1,B1),D2(L2,B2)"
		enc = str + "@0|L1@8|L2@12|B1@16|D1@20|B2@32|D2@36|??@48"
	case "SS-c":
		mnemonic += " D1(L1,B1),D2(B2),I3"
		enc = str + "@0|L1@8|I3@12|B1@16|D1@20|B2@32|D2@36|??@48"
	case "SS-d":
		mnemonic += " D1(R1,B1),D2(B2),R3"
		enc = str + "@0|R1@8|R3@12|B1@16|D1@20|B2@32|D2@36|??@48"
	case "SS-e":
		switch mnemonic {
		case "LMD":
			mnemonic += " R1,R3,D2(B2),D4(B4)"
		default:
			mnemonic += " R1,D2(B2),R3,D4(B4)"
		}
		enc = str + "@0|R1@8|R3@12|B2@16|D2@20|B4@32|D4@36|??@48"
	case "SS-f":
		mnemonic += " D1(B1),D2(L2,B2)"
		enc = str + "@0|L2@8|B1@16|D1@20|B2@32|D2@36|??@48"
	}
	return mnemonic, enc

}

func mnemonic_SSE(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " D1(B1),D2(B2)"
	val, _ := strconv.ParseUint(opcode, 16, 16)
	str := strconv.Itoa(int(val))
	enc = str + "@0|B1@16|D1@20|B2@32|D2@36|??@48"
	return mnemonic, enc
}

func mnemonic_SSF(mnemonic, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch mnemonic {
	case "LPD", "LPDG":
		mnemonic += " R3,D1(B1),D2(B2)"
	default:
		mnemonic += " D1(B1),D2(B2),R3"
	}
	enc = str1 + "@0|R3@8|" + str2 + "@12|B1@16|D1@20|B2@32|D2@36|??@48"
	return mnemonic, enc
}

func mnemonic_VRI(mnemonic, format, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch format {
	case "VRI-a":
		if strings.Contains(mnemonic, "VGBM") { // Check for M3 field
			mnemonic += " V1,I2"
			enc = str1 + "@0|V1@8|//@12|I2@16|//@32|RXB@36|" + str2 + "@40|??@48"
		} else {
			mnemonic += " V1,I2,M3"
			enc = str1 + "@0|V1@8|//@12|I2@16|M3@32|RXB@36|" + str2 + "@40|??@48"
		}
	case "VRI-b":
		mnemonic += " V1,I2,I3,M4"
		enc = str1 + "@0|V1@8|//@12|I2@16|I3@24|M4@32|RXB@36|" + str2 + "@40|??@48"
	case "VRI-c":
		mnemonic += " V1,V3,I2,M4"
		enc = str1 + "@0|V1@8|V3@12|I2@16|M4@32|RXB@36|" + str2 + "@40|??@48"
	case "VRI-d":
		if strings.Contains(mnemonic, "VERIM") { // Check for M5 field
			mnemonic += " V1,V2,V3,I4,M5"
			enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|I4@24|M5@32|RXB@36|" + str2 + "@40|??@48"
		} else {
			mnemonic += " V1,V2,V3,I4"
			enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|I4@24|//@32|RXB@36|" + str2 + "@40|??@48"
		}
	case "VRI-e":
		mnemonic += " V1,V2,I3,M4,M5"
		enc = str1 + "@0|V1@8|V2@12|I3@16|M5@28|M4@32|RXB@36|" + str2 + "@40|??@48"
	case "VRI-f":
		mnemonic += " V1,V2,V3,I4,M5"
		enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|M5@24|I4@28|RXB@36|" + str2 + "@40|??@48"
	case "VRI-g":
		mnemonic += " V1,V2,I3,I4,M5"
		enc = str1 + "@0|V1@8|V2@12|I4@16|M5@24|I3@28|RXB@36|" + str2 + "@40|??@48"
	case "VRI-h":
		mnemonic += " V1,I2,I3"
		enc = str1 + "@0|V1@8|//@12|I2@16|I3@32|RXB@36|" + str2 + "@40|??@48"
	case "VRI-i":
		mnemonic += " V1,R2,I3,M4"
		enc = str1 + "@0|V1@8|R2@12|//@16|M4@24|I3@28|RXB@36|" + str2 + "@40|??@48"
	}
	return mnemonic, enc
}

func mnemonic_VRR(mnemonic, format, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch format {
	case "VRR-a":
		switch mnemonic {
		case "VLR", "VTM": // V1,V2
			mnemonic += " V1,V2"
			enc = str1 + "@0|V1@8|V2@12|//@16|RXB@36|" + str2 + "@40|??@48"

		case "VSEG", "VUPH", "VUPLH", "VUPL", "VUPLL", "VCLZ", "VCTZ", "VEC", "VECL", "VLC", "VLP", "VPOPCT": // V1,V2,M3
			mnemonic += " V1,V2,M3"
			enc = str1 + "@0|V1@8|V2@12|//@16|M3@32|RXB@36|" + str2 + "@40|??@48"

		case "VISTR": // V1,V2,M3,M5
			mnemonic += " V1,V2,M3,M5"
			enc = str1 + "@0|V1@8|V2@12|//@16|M5@24|//@28|M3@32|RXB@36|" + str2 + "@40|??@48"

		case "WFC", "WFK", "VFLL", "VFSQ", "VCLFNH", "VCLFNL", "VCFN", "VCNF": // V1,V2,M3,M4
			mnemonic += " V1,V2,M3,M4"
			enc = str1 + "@0|V1@8|V2@12|//@16|M4@28|M3@32|RXB@36|" + str2 + "@40|??@48"

		case "VCFPS", "VCDG", "VCDLG", "VCGD", "VCFPL", "VCSFP", "VCLFP", "VCLGD", "VFI", "VFLR", "VFPSO": // V1,V2,M3,M4,M5
			mnemonic += " V1,V2,M3,M4,M5"
			enc = str1 + "@0|V1@8|V2@12|//@16|M5@24|M4@28|M3@32|RXB@36|" + str2 + "@40|??@48"
		}
	case "VRR-b":
		switch mnemonic {
		case "VSCSHP":
			mnemonic += " V1,V2,V3"
			enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|RXB@36|" + str2 + "@40|??@48"
		default:
			mnemonic += " V1,V2,V3,M4,M5"
			enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|M5@24|//@28|M4@32|RXB@36|" + str2 + "@40|??@48"
		}
	case "VRR-c":
		switch mnemonic {
		case "VFA", "VFD", "VFM", "VFS", "VCRNF": // V1,V2,V3,M4,M5
			mnemonic += " V1,V2,V3,M4,M5"
			enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|M5@28|M4@32|RXB@36|" + str2 + "@40|??@48"

		case "VFCE", "VFCH", "VFCHE", "VFMAX", "VFMIN": // V1,V2,V3,M4,M5,M6
			mnemonic += " V1,V2,V3,M4,M5,M6"
			enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|M6@24|M5@28|M4@32|RXB@36|" + str2 + "@40|??@48"

		case "VBPERM", "VN", "VNC", "VCKSM", "VX", "VNN", "VNO", "VNX",
			"VO", "VOC", "VSL", "VSLB", "VSRA", "VSRAB", "VSRL", "VSRLB": // V1,V2,V3
			mnemonic += " V1,V2,V3"
			enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|RXB@36|" + str2 + "@40|??@48"
		default: // V1,V2,V3,M4
			mnemonic += " V1,V2,V3,M4"
			enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|M4@32|RXB@36|" + str2 + "@40|??@48"
		}
	case "VRR-d":
		switch mnemonic {
		case "VMSL", "VSTRC", "VSTRS": // V1,V2,V3,V4,M5,M6
			mnemonic += " V1,V2,V3,V4,M5,M6"
			enc = str1 + "@0|V1@8|V2@12|V3@16|M5@20|M6@24|//@28|V4@32|RXB@36|" + str2 + "@40|??@48"
		default:
			mnemonic += " V1,V2,V3,V4,M5"
			enc = str1 + "@0|V1@8|V2@12|V3@16|M5@20|//@24|V4@32|RXB@36|" + str2 + "@40|??@48"
		}
	case "VRR-e":
		switch mnemonic {
		case "VPERM", "VSEL":
			mnemonic += " V1,V2,V3,V4"
			enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|V4@32|RXB@36|" + str2 + "@40|??@48"
		default:
			mnemonic += " V1,V2,V3,V4,M5,M6"
			enc = str1 + "@0|V1@8|V2@12|V3@16|M6@20|//@24|M5@28|V4@32|RXB@36|" + str2 + "@40|??@48"
		}
	case "VRR-f":
		mnemonic += " V1,R2,R3"
		enc = str1 + "@0|V1@8|R2@12|R3@16|//@20|RXB@36|" + str2 + "@40|??@48"
	case "VRR-g":
		mnemonic += " V1"
		enc = str1 + "@0|//@8|V1@12|//@16|RXB@36|" + str2 + "@40|??@48"
	case "VRR-h":
		mnemonic += " V1,V2,M3"
		enc = str1 + "@0|//@8|V1@12|V2@16|//@20|M3@24|//@28|RXB@36|" + str2 + "@40|??@48"
	case "VRR-i":
		mnemonic += " R1,V2,M3,M4"
		enc = str1 + "@0|R1@8|V2@12|//@16|M3@24|M4@28|//@32|RXB@36|" + str2 + "@40|??@48"
	case "VRR-j":
		mnemonic += " V1,V2,V3,M4"
		enc = str1 + "@0|V1@8|V2@12|V3@16|//@20|M4@24|//@28|RXB@36|" + str2 + "@40|??@48"
	case "VRR-k":
		mnemonic += " V1,V2,M3"
		enc = str1 + "@0|V1@8|V2@12|//@16|M3@24|//@28|RXB@36|" + str2 + "@40|??@48"
	}
	return mnemonic, enc
}

func mnemonic_VRS(mnemonic, format, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	switch format {
	case "VRS-a":
		mnemonic += " V1,V3,D2(B2),M4"
		enc = str1 + "@0|V1@8|V3@12|B2@16|D2@20|M4@32|RXB@36|" + str2 + "@40|??@48"
	case "VRS-b":
		if strings.Contains(mnemonic, "VLVG") {
			mnemonic += " V1,R3,D2(B2),M4"
			enc = str1 + "@0|V1@8|R3@12|B2@16|D2@20|M4@32|RXB@36|" + str2 + "@40|??@48"
		} else {
			mnemonic += " V1,R3,D2(B2)"
			enc = str1 + "@0|V1@8|R3@12|B2@16|D2@20|//@32|RXB@36|" + str2 + "@40|??@48"
		}
	case "VRS-c":
		mnemonic += " R1,V3,D2(B2),M4"
		enc = str1 + "@0|R1@8|V3@12|B2@16|D2@20|M4@32|RXB@36|" + str2 + "@40|??@48"
	case "VRS-d":
		mnemonic += " V1,R3,D2(B2)"
		enc = str1 + "@0|//@8|R3@12|B2@16|D2@20|V1@32|RXB@36|" + str2 + "@40|??@48"
	}
	return mnemonic, enc
}

func mnemonic_VRV(mnemonic, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	mnemonic += " V1,D2(V2,B2),M3"
	enc = str1 + "@0|V1@8|V2@12|B2@16|D2@20|M3@32|RXB@36|" + str2 + "@40|??@48"
	return mnemonic, enc
}

func mnemonic_VRX(mnemonic, opcode string) (string, string) {
	var enc string
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	mnemonic += " V1,D2(X2,B2),M3"
	enc = str1 + "@0|V1@8|X2@12|B2@16|D2@20|M3@32|RXB@36|" + str2 + "@40|??@48"
	return mnemonic, enc
}

func mnemonic_VSI(mnemonic, opcode string) (string, string) {
	var enc string
	mnemonic += " V1,D2(B2),I3"
	val1, _ := strconv.ParseUint(opcode[:2], 16, 16)
	str1 := strconv.Itoa(int(val1))
	val2, _ := strconv.ParseUint(opcode[2:], 16, 16)
	str2 := strconv.Itoa(int(val2))
	enc = str1 + "@0|I3@8|B2@16|D2@20|V1@32|RXB@36|" + str2 + "@40|??@48"
	return mnemonic, enc
}

func matchXCord(t pdf.Text, Xcord float64) bool {
	return math.Abs(t.X-Xcord) < 0.9
}

func match(t pdf.Text, font string, size float64, substr string) bool {
	return t.Font == font && (size == 0 || math.Abs(t.FontSize-size) < 0.2) && strings.Contains(t.S, substr)
}

func findWords(chars []pdf.Text) (words []pdf.Text) {
	// Sort by Y coordinate and normalize.
	const nudge = 1.5
	sort.Sort(pdf.TextVertical(chars))
	old := -100000.0
	for i, c := range chars {
		if c.Y != old && math.Abs(old-c.Y) < nudge {
			chars[i].Y = old
		} else {
			old = c.Y
		}
	}

	// Sort by Y coordinate, breaking ties with X.
	// This will bring letters in a single word together.
	sort.Sort(pdf.TextVertical(chars))

	// Loop over chars.
	for i := 0; i < len(chars); {
		// Find all chars on line.
		j := i + 1
		for j < len(chars) && chars[j].Y == chars[i].Y {
			j++
		}
		var end float64
		// Split line into words (really, phrases).
		for k := i; k < j; {
			ck := &chars[k]
			s := ck.S
			end = ck.X + ck.W
			charSpace := ck.FontSize / 6
			wordSpace := ck.FontSize * 2 / 3
			l := k + 1
			for l < j {
				// Grow word.
				cl := &chars[l]
				if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+charSpace {
					s += cl.S
					end = cl.X + cl.W
					l++
					continue
				}
				// Add space to phrase before next word.
				if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+wordSpace {
					s += " " + cl.S
					end = cl.X + cl.W
					l++
					continue
				}
				break
			}
			f := ck.Font
			f = strings.TrimSuffix(f, ",Italic")
			f = strings.TrimSuffix(f, "-Italic")
			words = append(words, pdf.Text{
				Font:     f,
				FontSize: ck.FontSize,
				X:        ck.X,
				Y:        ck.Y,
				W:        end - ck.X,
				S:        s,
			})
			k = l
		}
		i = j
	}
	return words
}

func sameFont(f1, f2 string) bool {
	f1 = strings.TrimSuffix(f1, ",Italic")
	f1 = strings.TrimSuffix(f1, "-Italic")
	f2 = strings.TrimSuffix(f1, ",Italic")
	f2 = strings.TrimSuffix(f1, "-Italic")
	return strings.TrimSuffix(f1, ",Italic") == strings.TrimSuffix(f2, ",Italic") || f1 == "Symbol" || f2 == "Symbol" || f1 == "TimesNewRoman" || f2 == "TimesNewRoman"
}
