162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Generate opcode table initializers for the in-kernel disassembler.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *    Copyright IBM Corp. 2017
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <stdlib.h>
1062306a36Sopenharmony_ci#include <string.h>
1162306a36Sopenharmony_ci#include <ctype.h>
1262306a36Sopenharmony_ci#include <stdio.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define STRING_SIZE_MAX 20
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct insn_type {
1762306a36Sopenharmony_ci	unsigned char byte;
1862306a36Sopenharmony_ci	unsigned char mask;
1962306a36Sopenharmony_ci	char **format;
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct insn {
2362306a36Sopenharmony_ci	struct insn_type *type;
2462306a36Sopenharmony_ci	char opcode[STRING_SIZE_MAX];
2562306a36Sopenharmony_ci	char name[STRING_SIZE_MAX];
2662306a36Sopenharmony_ci	char upper[STRING_SIZE_MAX];
2762306a36Sopenharmony_ci	char format[STRING_SIZE_MAX];
2862306a36Sopenharmony_ci	unsigned int name_len;
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct insn_group {
3262306a36Sopenharmony_ci	struct insn_type *type;
3362306a36Sopenharmony_ci	int offset;
3462306a36Sopenharmony_ci	int count;
3562306a36Sopenharmony_ci	char opcode[2];
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct insn_format {
3962306a36Sopenharmony_ci	char *format;
4062306a36Sopenharmony_ci	int type;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct gen_opcode {
4462306a36Sopenharmony_ci	struct insn *insn;
4562306a36Sopenharmony_ci	int nr;
4662306a36Sopenharmony_ci	struct insn_group *group;
4762306a36Sopenharmony_ci	int nr_groups;
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * Table of instruction format types. Each opcode is defined with at
5262306a36Sopenharmony_ci * least one byte (two nibbles), three nibbles, or two bytes (four
5362306a36Sopenharmony_ci * nibbles).
5462306a36Sopenharmony_ci * The byte member of each instruction format type entry defines
5562306a36Sopenharmony_ci * within which byte of an instruction the third (and fourth) nibble
5662306a36Sopenharmony_ci * of an opcode can be found. The mask member is the and-mask that
5762306a36Sopenharmony_ci * needs to be applied on this byte in order to get the third (and
5862306a36Sopenharmony_ci * fourth) nibble of the opcode.
5962306a36Sopenharmony_ci * The format array defines all instruction formats (as defined in the
6062306a36Sopenharmony_ci * Principles of Operation) which have the same position of the opcode
6162306a36Sopenharmony_ci * nibbles.
6262306a36Sopenharmony_ci * A special case are instruction formats with 1-byte opcodes. In this
6362306a36Sopenharmony_ci * case the byte member always is zero, so that the mask is applied on
6462306a36Sopenharmony_ci * the (only) byte that contains the opcode.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cistatic struct insn_type insn_type_table[] = {
6762306a36Sopenharmony_ci	{
6862306a36Sopenharmony_ci		.byte = 0,
6962306a36Sopenharmony_ci		.mask = 0xff,
7062306a36Sopenharmony_ci		.format = (char *[]) {
7162306a36Sopenharmony_ci			"MII",
7262306a36Sopenharmony_ci			"RR",
7362306a36Sopenharmony_ci			"RS",
7462306a36Sopenharmony_ci			"RSI",
7562306a36Sopenharmony_ci			"RX",
7662306a36Sopenharmony_ci			"SI",
7762306a36Sopenharmony_ci			"SMI",
7862306a36Sopenharmony_ci			"SS",
7962306a36Sopenharmony_ci			NULL,
8062306a36Sopenharmony_ci		},
8162306a36Sopenharmony_ci	},
8262306a36Sopenharmony_ci	{
8362306a36Sopenharmony_ci		.byte = 1,
8462306a36Sopenharmony_ci		.mask = 0x0f,
8562306a36Sopenharmony_ci		.format = (char *[]) {
8662306a36Sopenharmony_ci			"RI",
8762306a36Sopenharmony_ci			"RIL",
8862306a36Sopenharmony_ci			"SSF",
8962306a36Sopenharmony_ci			NULL,
9062306a36Sopenharmony_ci		},
9162306a36Sopenharmony_ci	},
9262306a36Sopenharmony_ci	{
9362306a36Sopenharmony_ci		.byte = 1,
9462306a36Sopenharmony_ci		.mask = 0xff,
9562306a36Sopenharmony_ci		.format = (char *[]) {
9662306a36Sopenharmony_ci			"E",
9762306a36Sopenharmony_ci			"IE",
9862306a36Sopenharmony_ci			"RRE",
9962306a36Sopenharmony_ci			"RRF",
10062306a36Sopenharmony_ci			"RRR",
10162306a36Sopenharmony_ci			"S",
10262306a36Sopenharmony_ci			"SIL",
10362306a36Sopenharmony_ci			"SSE",
10462306a36Sopenharmony_ci			NULL,
10562306a36Sopenharmony_ci		},
10662306a36Sopenharmony_ci	},
10762306a36Sopenharmony_ci	{
10862306a36Sopenharmony_ci		.byte = 5,
10962306a36Sopenharmony_ci		.mask = 0xff,
11062306a36Sopenharmony_ci		.format = (char *[]) {
11162306a36Sopenharmony_ci			"RIE",
11262306a36Sopenharmony_ci			"RIS",
11362306a36Sopenharmony_ci			"RRS",
11462306a36Sopenharmony_ci			"RSE",
11562306a36Sopenharmony_ci			"RSL",
11662306a36Sopenharmony_ci			"RSY",
11762306a36Sopenharmony_ci			"RXE",
11862306a36Sopenharmony_ci			"RXF",
11962306a36Sopenharmony_ci			"RXY",
12062306a36Sopenharmony_ci			"SIY",
12162306a36Sopenharmony_ci			"VRI",
12262306a36Sopenharmony_ci			"VRR",
12362306a36Sopenharmony_ci			"VRS",
12462306a36Sopenharmony_ci			"VRV",
12562306a36Sopenharmony_ci			"VRX",
12662306a36Sopenharmony_ci			"VSI",
12762306a36Sopenharmony_ci			NULL,
12862306a36Sopenharmony_ci		},
12962306a36Sopenharmony_ci	},
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic struct insn_type *insn_format_to_type(char *format)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	char tmp[STRING_SIZE_MAX];
13562306a36Sopenharmony_ci	char *base_format, **ptr;
13662306a36Sopenharmony_ci	int i;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	strcpy(tmp, format);
13962306a36Sopenharmony_ci	base_format = tmp;
14062306a36Sopenharmony_ci	base_format = strsep(&base_format, "_");
14162306a36Sopenharmony_ci	for (i = 0; i < sizeof(insn_type_table) / sizeof(insn_type_table[0]); i++) {
14262306a36Sopenharmony_ci		ptr = insn_type_table[i].format;
14362306a36Sopenharmony_ci		while (*ptr) {
14462306a36Sopenharmony_ci			if (!strcmp(base_format, *ptr))
14562306a36Sopenharmony_ci				return &insn_type_table[i];
14662306a36Sopenharmony_ci			ptr++;
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	exit(EXIT_FAILURE);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void read_instructions(struct gen_opcode *desc)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct insn insn;
15562306a36Sopenharmony_ci	int rc, i;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	while (1) {
15862306a36Sopenharmony_ci		rc = scanf("%s %s %s", insn.opcode, insn.name, insn.format);
15962306a36Sopenharmony_ci		if (rc == EOF)
16062306a36Sopenharmony_ci			break;
16162306a36Sopenharmony_ci		if (rc != 3)
16262306a36Sopenharmony_ci			exit(EXIT_FAILURE);
16362306a36Sopenharmony_ci		insn.type = insn_format_to_type(insn.format);
16462306a36Sopenharmony_ci		insn.name_len = strlen(insn.name);
16562306a36Sopenharmony_ci		for (i = 0; i <= insn.name_len; i++)
16662306a36Sopenharmony_ci			insn.upper[i] = toupper((unsigned char)insn.name[i]);
16762306a36Sopenharmony_ci		desc->nr++;
16862306a36Sopenharmony_ci		desc->insn = realloc(desc->insn, desc->nr * sizeof(*desc->insn));
16962306a36Sopenharmony_ci		if (!desc->insn)
17062306a36Sopenharmony_ci			exit(EXIT_FAILURE);
17162306a36Sopenharmony_ci		desc->insn[desc->nr - 1] = insn;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int cmpformat(const void *a, const void *b)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	return strcmp(((struct insn *)a)->format, ((struct insn *)b)->format);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void print_formats(struct gen_opcode *desc)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	char *format;
18362306a36Sopenharmony_ci	int i, count;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	qsort(desc->insn, desc->nr, sizeof(*desc->insn), cmpformat);
18662306a36Sopenharmony_ci	format = "";
18762306a36Sopenharmony_ci	count = 0;
18862306a36Sopenharmony_ci	printf("enum {\n");
18962306a36Sopenharmony_ci	for (i = 0; i < desc->nr; i++) {
19062306a36Sopenharmony_ci		if (!strcmp(format, desc->insn[i].format))
19162306a36Sopenharmony_ci			continue;
19262306a36Sopenharmony_ci		count++;
19362306a36Sopenharmony_ci		format = desc->insn[i].format;
19462306a36Sopenharmony_ci		printf("\tINSTR_%s,\n", format);
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci	printf("}; /* %d */\n\n", count);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int cmp_long_insn(const void *a, const void *b)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	return strcmp(((struct insn *)a)->name, ((struct insn *)b)->name);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void print_long_insn(struct gen_opcode *desc)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct insn *insn;
20762306a36Sopenharmony_ci	int i, count;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	qsort(desc->insn, desc->nr, sizeof(*desc->insn), cmp_long_insn);
21062306a36Sopenharmony_ci	count = 0;
21162306a36Sopenharmony_ci	printf("enum {\n");
21262306a36Sopenharmony_ci	for (i = 0; i < desc->nr; i++) {
21362306a36Sopenharmony_ci		insn = &desc->insn[i];
21462306a36Sopenharmony_ci		if (insn->name_len < 6)
21562306a36Sopenharmony_ci			continue;
21662306a36Sopenharmony_ci		printf("\tLONG_INSN_%s,\n", insn->upper);
21762306a36Sopenharmony_ci		count++;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci	printf("}; /* %d */\n\n", count);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	printf("#define LONG_INSN_INITIALIZER { \\\n");
22262306a36Sopenharmony_ci	for (i = 0; i < desc->nr; i++) {
22362306a36Sopenharmony_ci		insn = &desc->insn[i];
22462306a36Sopenharmony_ci		if (insn->name_len < 6)
22562306a36Sopenharmony_ci			continue;
22662306a36Sopenharmony_ci		printf("\t[LONG_INSN_%s] = \"%s\", \\\n", insn->upper, insn->name);
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci	printf("}\n\n");
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void print_opcode(struct insn *insn, int nr)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	char *opcode;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	opcode = insn->opcode;
23662306a36Sopenharmony_ci	if (insn->type->byte != 0)
23762306a36Sopenharmony_ci		opcode += 2;
23862306a36Sopenharmony_ci	printf("\t[%4d] = { .opfrag = 0x%s, .format = INSTR_%s, ", nr, opcode, insn->format);
23962306a36Sopenharmony_ci	if (insn->name_len < 6)
24062306a36Sopenharmony_ci		printf(".name = \"%s\" ", insn->name);
24162306a36Sopenharmony_ci	else
24262306a36Sopenharmony_ci		printf(".offset = LONG_INSN_%s ", insn->upper);
24362306a36Sopenharmony_ci	printf("}, \\\n");
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic void add_to_group(struct gen_opcode *desc, struct insn *insn, int offset)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct insn_group *group;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	group = desc->group ? &desc->group[desc->nr_groups - 1] : NULL;
25162306a36Sopenharmony_ci	if (group && (!strncmp(group->opcode, insn->opcode, 2) || group->type->byte == 0)) {
25262306a36Sopenharmony_ci		group->count++;
25362306a36Sopenharmony_ci		return;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	desc->nr_groups++;
25662306a36Sopenharmony_ci	desc->group = realloc(desc->group, desc->nr_groups * sizeof(*desc->group));
25762306a36Sopenharmony_ci	if (!desc->group)
25862306a36Sopenharmony_ci		exit(EXIT_FAILURE);
25962306a36Sopenharmony_ci	group = &desc->group[desc->nr_groups - 1];
26062306a36Sopenharmony_ci	memcpy(group->opcode, insn->opcode, 2);
26162306a36Sopenharmony_ci	group->type = insn->type;
26262306a36Sopenharmony_ci	group->offset = offset;
26362306a36Sopenharmony_ci	group->count = 1;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int cmpopcode(const void *a, const void *b)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	return strcmp(((struct insn *)a)->opcode, ((struct insn *)b)->opcode);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void print_opcode_table(struct gen_opcode *desc)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	char opcode[2] = "";
27462306a36Sopenharmony_ci	struct insn *insn;
27562306a36Sopenharmony_ci	int i, offset;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	qsort(desc->insn, desc->nr, sizeof(*desc->insn), cmpopcode);
27862306a36Sopenharmony_ci	printf("#define OPCODE_TABLE_INITIALIZER { \\\n");
27962306a36Sopenharmony_ci	offset = 0;
28062306a36Sopenharmony_ci	for (i = 0; i < desc->nr; i++) {
28162306a36Sopenharmony_ci		insn = &desc->insn[i];
28262306a36Sopenharmony_ci		if (insn->type->byte == 0)
28362306a36Sopenharmony_ci			continue;
28462306a36Sopenharmony_ci		add_to_group(desc, insn, offset);
28562306a36Sopenharmony_ci		if (strncmp(opcode, insn->opcode, 2)) {
28662306a36Sopenharmony_ci			memcpy(opcode, insn->opcode, 2);
28762306a36Sopenharmony_ci			printf("\t/* %.2s */ \\\n", opcode);
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci		print_opcode(insn, offset);
29062306a36Sopenharmony_ci		offset++;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci	printf("\t/* 1-byte opcode instructions */ \\\n");
29362306a36Sopenharmony_ci	for (i = 0; i < desc->nr; i++) {
29462306a36Sopenharmony_ci		insn = &desc->insn[i];
29562306a36Sopenharmony_ci		if (insn->type->byte != 0)
29662306a36Sopenharmony_ci			continue;
29762306a36Sopenharmony_ci		add_to_group(desc, insn, offset);
29862306a36Sopenharmony_ci		print_opcode(insn, offset);
29962306a36Sopenharmony_ci		offset++;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci	printf("}\n\n");
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic void print_opcode_table_offsets(struct gen_opcode *desc)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct insn_group *group;
30762306a36Sopenharmony_ci	int i;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	printf("#define OPCODE_OFFSET_INITIALIZER { \\\n");
31062306a36Sopenharmony_ci	for (i = 0; i < desc->nr_groups; i++) {
31162306a36Sopenharmony_ci		group = &desc->group[i];
31262306a36Sopenharmony_ci		printf("\t{ .opcode = 0x%.2s, .mask = 0x%02x, .byte = %d, .offset = %d, .count = %d }, \\\n",
31362306a36Sopenharmony_ci		       group->opcode, group->type->mask, group->type->byte, group->offset, group->count);
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci	printf("}\n\n");
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ciint main(int argc, char **argv)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct gen_opcode _desc = { 0 };
32162306a36Sopenharmony_ci	struct gen_opcode *desc = &_desc;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	read_instructions(desc);
32462306a36Sopenharmony_ci	printf("#ifndef __S390_GENERATED_DIS_DEFS_H__\n");
32562306a36Sopenharmony_ci	printf("#define __S390_GENERATED_DIS_DEFS_H__\n");
32662306a36Sopenharmony_ci	printf("/*\n");
32762306a36Sopenharmony_ci	printf(" * DO NOT MODIFY.\n");
32862306a36Sopenharmony_ci	printf(" *\n");
32962306a36Sopenharmony_ci	printf(" * This file was generated by %s\n", __FILE__);
33062306a36Sopenharmony_ci	printf(" */\n\n");
33162306a36Sopenharmony_ci	print_formats(desc);
33262306a36Sopenharmony_ci	print_long_insn(desc);
33362306a36Sopenharmony_ci	print_opcode_table(desc);
33462306a36Sopenharmony_ci	print_opcode_table_offsets(desc);
33562306a36Sopenharmony_ci	printf("#endif\n");
33662306a36Sopenharmony_ci	exit(EXIT_SUCCESS);
33762306a36Sopenharmony_ci}
338