summaryrefslogtreecommitdiff
path: root/gen/docs
diff options
context:
space:
mode:
authorallexanderbergmans <allexander.bergmans@student.elisa.be>2026-07-03 12:17:10 +0200
committerallexanderbergmans <allexander.bergmans@student.elisa.be>2026-07-03 12:17:10 +0200
commit887875959aa84af92291db334898aaa20956e632 (patch)
tree62f68d6e93cf444e5605a40c3e8ea7ec0bd89f49 /gen/docs
Diffstat (limited to 'gen/docs')
-rw-r--r--gen/docs/convert.sh55
-rw-r--r--gen/docs/generator.c481
-rw-r--r--gen/docs/generator.h9
-rwxr-xr-xgen/docs/isa-docgenbin0 -> 51736 bytes
-rw-r--r--gen/docs/isa_defs/arch.isa68
-rw-r--r--gen/docs/isa_defs/csrs.isa191
-rw-r--r--gen/docs/isa_defs/instructions.isa512
-rw-r--r--gen/docs/isa_defs/registers.isa289
-rw-r--r--gen/docs/main.c83
-rw-r--r--gen/docs/parser.c256
-rw-r--r--gen/docs/parser.h9
-rw-r--r--gen/docs/style.css139
-rw-r--r--gen/docs/types.h78
13 files changed, 2170 insertions, 0 deletions
diff --git a/gen/docs/convert.sh b/gen/docs/convert.sh
new file mode 100644
index 0000000..047eb63
--- /dev/null
+++ b/gen/docs/convert.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+# convert.sh — Convert generated Markdown ISA doc to PDF
+# Usage: ./convert.sh [input.md] [output.pdf]
+#
+# Requires: pandoc + xelatex (or wkhtmltopdf)
+# Install: brew install pandoc mactex (macOS)
+# apt install pandoc texlive (Linux)
+
+set -e
+
+INPUT="${1:-../../doc/spec/isa_reference.md}"
+OUTPUT="${2:-../../doc/spec/isa_reference.pdf}"
+DIR="$(cd "$(dirname "$OUTPUT")" && pwd)"
+BASE="$(basename "$OUTPUT")"
+
+echo "Converting: $INPUT → $OUTPUT"
+
+if command -v pandoc >/dev/null 2>&1; then
+ if command -v xelatex >/dev/null 2>&1; then
+ pandoc "$INPUT" \
+ -o "$OUTPUT" \
+ --pdf-engine=xelatex \
+ -V geometry:margin=1in \
+ -V colorlinks=true \
+ -V linkcolor=blue \
+ -V toccolor=blue \
+ -V mainfont="Times New Roman" \
+ -V monofont="Courier New" \
+ --toc --toc-depth=3 \
+ --highlight-style=tango
+ echo "PDF generated: $OUTPUT"
+ elif command -v wkhtmltopdf >/dev/null 2>&1; then
+ DIR="$(dirname "$INPUT")"
+ pandoc "$INPUT" \
+ -o "${DIR}/isa_reference.html" \
+ --self-contained \
+ --toc --toc-depth=3
+ wkhtmltopdf \
+ --margin-top 20mm \
+ --margin-bottom 20mm \
+ --margin-left 15mm \
+ --margin-right 15mm \
+ --toc \
+ "${DIR}/isa_reference.html" "$OUTPUT"
+ echo "PDF generated: $OUTPUT"
+ else
+ echo "Error: No PDF engine found."
+ echo "Install: brew install pandoc mactex (macOS)"
+ echo " or: apt install pandoc texlive-latex-base (Linux)"
+ exit 1
+ fi
+else
+ echo "Error: pandoc not found. Install it first."
+ exit 1
+fi
diff --git a/gen/docs/generator.c b/gen/docs/generator.c
new file mode 100644
index 0000000..f67e42f
--- /dev/null
+++ b/gen/docs/generator.c
@@ -0,0 +1,481 @@
+#include "generator.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void write_title_page(FILE *out, const IsaDb *db) {
+ fprintf(out, "---\n");
+ fprintf(out, "title: \"%s Instruction Set Architecture\"\n", db->arch_name[0] ? db->arch_name : "Fyntv");
+ fprintf(out, "subtitle: \"Reference Manual\"\n");
+ fprintf(out, "version: \"%s\"\n", db->arch_version[0] ? db->arch_version : "1.0.0");
+ fprintf(out, "date: \"%s\"\n", db->arch_date[0] ? db->arch_date : "June 2026");
+ fprintf(out, "status: \"%s\"\n", db->arch_status[0] ? db->arch_status : "Draft");
+ fprintf(out, "geometry: margin=1in\n");
+ fprintf(out, "toc: true\n");
+ fprintf(out, "numbersections: true\n");
+ fprintf(out, "---\n\n");
+}
+
+static void write_revision_history(FILE *out, const IsaDb *db) {
+ fprintf(out, "\\newpage\n\n");
+ fprintf(out, "# Revision History\n\n");
+ fprintf(out, "| Version | Date | Description |\n");
+ fprintf(out, "|---------|------|-------------|\n");
+ fprintf(out, "| %s | %s | Initial %s release |\n",
+ db->arch_version[0] ? db->arch_version : "1.0.0",
+ db->arch_date[0] ? db->arch_date : "2026-06",
+ db->arch_name[0] ? db->arch_name : "ISA");
+ fprintf(out, "\n\\newpage\n\n");
+}
+
+static void write_toc_placeholder(FILE *out) {
+ fprintf(out, "\\tableofcontents\n\n");
+ fprintf(out, "\\newpage\n\n");
+}
+
+static void write_intro(FILE *out, const IsaDb *db) {
+ fprintf(out, "# Introduction\n\n");
+ fprintf(out, "This document defines the **%s Instruction Set Architecture (ISA)** version %s.\n\n",
+ db->arch_name[0] ? db->arch_name : "Fyntv",
+ db->arch_version[0] ? db->arch_version : "1.0.0");
+
+ fprintf(out, "## Scope\n\n");
+ fprintf(out, "This specification covers:\n\n");
+ fprintf(out, "- Instruction formats and encoding\n");
+ fprintf(out, "- Register file and calling convention\n");
+ fprintf(out, "- All base and extension instructions\n");
+ fprintf(out, "- Control and status registers (CSRs)\n");
+ fprintf(out, "- Exception and trap handling\n\n");
+
+ fprintf(out, "## ISA Overview\n\n");
+ fprintf(out, "The %s ISA is a load-store architecture with:\n\n",
+ db->arch_name[0] ? db->arch_name : "Fyntv");
+ fprintf(out, "- 32 general-purpose registers (x0–x31)\n");
+ fprintf(out, "- Fixed-width 32-bit instructions\n");
+ fprintf(out, "- Three operand formats: register (R-type), immediate (I-type), and store (S-type)\n");
+ fprintf(out, "- Branch (B-type) and upper-immediate (U-type) variants\n");
+ fprintf(out, "- A separate jump-and-link (J-type) format\n\n");
+ fprintf(out, "\\newpage\n\n");
+}
+
+static void write_registers(FILE *out, const IsaDb *db) {
+ fprintf(out, "# Registers\n\n");
+ fprintf(out, "## General-Purpose Registers\n\n");
+ fprintf(out, "The architecture provides 32 general-purpose registers (x0–x31), "
+ "each 32 bits wide. Register x0 is hardwired to the constant zero.\n\n");
+
+ fprintf(out, "| Register | ABI Name | Description | Callee-Saved |\n");
+ fprintf(out, "|----------|----------|-------------|--------------|\n");
+ for (int i = 0; i < db->num_registers; i++) {
+ const IsaRegister *r = &db->registers[i];
+ fprintf(out, "| %s | %s | %s | %s |\n",
+ r->name, r->abbr[0] ? r->abbr : "—",
+ r->desc[0] ? r->desc : "—",
+ r->preserve ? "Yes" : (r->preserve == 0 && r->index != 0 ? "No" : "—"));
+ }
+
+ fprintf(out, "\n### ABI Calling Convention\n\n");
+ fprintf(out, "| Register | ABI Name | Caller-Saved | Argument |\n");
+ fprintf(out, "|----------|----------|-------------|----------|\n");
+ for (int i = 0; i < db->num_registers; i++) {
+ const IsaRegister *r = &db->registers[i];
+ fprintf(out, "| %s | %s | %s | %s |\n",
+ r->name, r->abbr[0] ? r->abbr : "—",
+ r->caller_saved ? "Yes" : "No",
+ r->arg_reg ? "Yes" : "No");
+ }
+ fprintf(out, "\n\\newpage\n\n");
+}
+
+static void write_format_diagram(FILE *out, const IsaFormat *fmt) {
+ fprintf(out, "### %s-Type Format\n\n", fmt->name);
+ fprintf(out, "Bit width: **%d bits**\n\n", fmt->width);
+ fprintf(out, "```\n");
+
+ for (int row = 0; row < 3; row++) {
+ for (int j = fmt->num_fields - 1; j >= 0; j--) {
+ int fw = fmt->fields[j].high - fmt->fields[j].low + 1;
+ if (j < fmt->num_fields - 1) fputc('|', out);
+
+ if (row == 0) {
+ int rpad = (fw - 5) / 2;
+ int lpad = fw - 5 - rpad;
+ if (rpad < 0) rpad = 0;
+ if (lpad < 0) lpad = 0;
+ for (int k = 0; k < rpad; k++) fputc(' ', out);
+ fprintf(out, "%d:%d", fmt->fields[j].high, fmt->fields[j].low);
+ for (int k = 0; k < lpad; k++) fputc(' ', out);
+ } else if (row == 1) {
+ int slen = (int)strlen(fmt->fields[j].name);
+ int rpad = (fw - slen) / 2;
+ int lpad = fw - slen - rpad;
+ if (rpad < 0) rpad = 0;
+ if (lpad < 0) lpad = 0;
+ for (int k = 0; k < rpad; k++) fputc(' ', out);
+ fprintf(out, "%s", fmt->fields[j].name);
+ for (int k = 0; k < lpad; k++) fputc(' ', out);
+ } else {
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d", fw);
+ int slen = (int)strlen(buf);
+ int rpad = (fw - slen) / 2;
+ int lpad = fw - slen - rpad;
+ if (rpad < 0) rpad = 0;
+ if (lpad < 0) lpad = 0;
+ for (int k = 0; k < rpad; k++) fputc(' ', out);
+ fprintf(out, "%s", buf);
+ for (int k = 0; k < lpad; k++) fputc(' ', out);
+ }
+ }
+ fprintf(out, "\n");
+ }
+
+ fprintf(out, "```\n\n");
+}
+
+static void write_formats(FILE *out, const IsaDb *db) {
+ fprintf(out, "# Instruction Formats\n\n");
+ fprintf(out, "Instructions are encoded in one of several fixed formats. "
+ "All formats share the same opcode placement (bits 6:0) to "
+ "allow efficient decoding.\n\n");
+
+ for (int i = 0; i < db->num_formats; i++) {
+ write_format_diagram(out, &db->formats[i]);
+ }
+
+ fprintf(out, "\\newpage\n\n");
+}
+
+static int cat_cmp(const void *a, const void *b) {
+ return strcasecmp(((const IsaInstruction *)a)->category,
+ ((const IsaInstruction *)b)->category);
+}
+
+static void write_instructions(FILE *out, const IsaDb *db) {
+ fprintf(out, "# Instruction Set\n\n");
+
+ if (db->num_instructions == 0) {
+ fprintf(out, "No instructions defined.\n\n");
+ return;
+ }
+
+ IsaInstruction *sorted = malloc(db->num_instructions * sizeof(IsaInstruction));
+ if (!sorted) return;
+ memcpy(sorted, db->instructions, db->num_instructions * sizeof(IsaInstruction));
+ qsort(sorted, db->num_instructions, sizeof(IsaInstruction), cat_cmp);
+
+ char current_cat[MAX_LABEL] = "";
+ for (int i = 0; i < db->num_instructions; i++) {
+ const IsaInstruction *inst = &sorted[i];
+ const char *cat = inst->category[0] ? inst->category : "Uncategorized";
+
+ if (strcasecmp(current_cat, cat) != 0) {
+ strncpy(current_cat, cat, MAX_LABEL - 1);
+ fprintf(out, "## %s Operations\n\n", cat);
+
+ fprintf(out, "| Mnemonic | Format | Opcode | Funct3 | Funct7 | Operands | Description |\n");
+ fprintf(out, "|----------|--------|--------|--------|--------|----------|-------------|\n");
+ }
+
+ char f3buf[16], f7buf[16];
+ if (inst->funct3_valid) snprintf(f3buf, sizeof(f3buf), "0x%X", inst->funct3);
+ else strcpy(f3buf, "—");
+ if (inst->funct7_valid) snprintf(f7buf, sizeof(f7buf), "0x%02X", inst->funct7);
+ else strcpy(f7buf, "—");
+
+ fprintf(out, "| `%s` | %s | `0x%02X` | %s | %s | %s | %s |\n",
+ inst->name,
+ inst->fmt_name,
+ inst->opcode,
+ f3buf, f7buf,
+ inst->operands[0] ? inst->operands : "—",
+ inst->desc);
+ }
+
+ fprintf(out, "\n\\newpage\n\n");
+
+ fprintf(out, "## Instruction Details\n\n");
+ strcpy(current_cat, "");
+ for (int i = 0; i < db->num_instructions; i++) {
+ const IsaInstruction *inst = &sorted[i];
+ const char *cat = inst->category[0] ? inst->category : "Uncategorized";
+
+ if (strcasecmp(current_cat, cat) != 0) {
+ strncpy(current_cat, cat, MAX_LABEL - 1);
+ }
+
+ fprintf(out, "### %s\n\n", inst->name);
+ fprintf(out, "- **Format:** %s\n", inst->fmt_name);
+ fprintf(out, "- **Opcode:** `0x%02X`\n", inst->opcode);
+ if (inst->funct3_valid)
+ fprintf(out, "- **Funct3:** `0x%X`\n", inst->funct3);
+ if (inst->funct7_valid)
+ fprintf(out, "- **Funct7:** `0x%02X`\n", inst->funct7);
+ fprintf(out, "- **Operands:** `%s`\n", inst->operands[0] ? inst->operands : "(none)");
+ fprintf(out, "- **Description:** %s\n", inst->desc);
+ if (inst->note[0])
+ fprintf(out, "- **Operation:** `%s`\n", inst->note);
+ fprintf(out, "\n");
+ }
+
+ free(sorted);
+ fprintf(out, "\\newpage\n\n");
+}
+
+static void write_opcode_map(FILE *out, const IsaDb *db) {
+ fprintf(out, "# Opcode Map\n\n");
+ fprintf(out, "The following table shows the top-level opcode mapping. "
+ "The opcode occupies bits 6:0 of every instruction.\n\n");
+
+ fprintf(out, "| opcode[6:0] | Type | Instructions |\n");
+ fprintf(out, "|-------------|------|-------------|\n");
+
+ int seen_ops[128] = {0};
+ for (int i = 0; i < db->num_instructions; i++) {
+ const IsaInstruction *inst = &db->instructions[i];
+ int op = inst->opcode & 0x7F;
+ if (seen_ops[op]) continue;
+ seen_ops[op] = 1;
+
+ fprintf(out, "| `0x%02X` (%d) | %s | %s", op, op, inst->fmt_name, inst->name);
+ int count = 1;
+ for (int j = i + 1; j < db->num_instructions; j++) {
+ if ((db->instructions[j].opcode & 0x7F) == (unsigned)op) {
+ fprintf(out, ", %s", db->instructions[j].name);
+ count++;
+ }
+ }
+ fprintf(out, " (%d inst)%s |\n", count, count > 1 ? "s" : "");
+ }
+
+ fprintf(out, "\n### Encoding Matrix\n\n");
+ fprintf(out, "| opcode \\ funct3 |");
+ for (int f3 = 0; f3 < 8; f3++) fprintf(out, " %X |", f3);
+ fprintf(out, "\n");
+ fprintf(out, "|");
+ for (int f3 = 0; f3 < 9; f3++) fprintf(out, "---|");
+ fprintf(out, "\n");
+
+ int seen_ops2[128] = {0};
+ for (int i = 0; i < db->num_instructions; i++) {
+ const IsaInstruction *inst = &db->instructions[i];
+ int op = inst->opcode & 0x7F;
+ if (seen_ops2[op]) continue;
+ seen_ops2[op] = 1;
+
+ fprintf(out, "| `0x%02X` |", op);
+ for (int f3 = 0; f3 < 8; f3++) {
+ int found = 0;
+ for (int j = 0; j < db->num_instructions; j++) {
+ if ((db->instructions[j].opcode & 0x7F) == (unsigned)op &&
+ db->instructions[j].funct3 == (unsigned)f3 &&
+ db->instructions[j].funct3_valid) {
+ fprintf(out, " %s |", db->instructions[j].name);
+ found = 1;
+ break;
+ }
+ }
+ if (!found) fprintf(out, " — |");
+ }
+ fprintf(out, "\n");
+ }
+
+ fprintf(out, "\n\\newpage\n\n");
+}
+
+static void write_csrs(FILE *out, const IsaDb *db) {
+ if (db->num_csrs == 0) return;
+
+ fprintf(out, "# Control and Status Registers\n\n");
+ fprintf(out, "The following CSRs are accessible via the `CSRRW`, `CSRRS`, "
+ "`CSRRC`, and their immediate variants.\n\n");
+
+ fprintf(out, "| Address | Name | Description |\n");
+ fprintf(out, "|---------|------|-------------|\n");
+ for (int i = 0; i < db->num_csrs; i++) {
+ fprintf(out, "| `0x%03X` | %s | %s |\n",
+ db->csrs[i].number, db->csrs[i].name, db->csrs[i].desc);
+ }
+ fprintf(out, "\n\\newpage\n\n");
+}
+
+static void write_exceptions(FILE *out) {
+ fprintf(out, "# Exception and Trap Handling\n\n");
+ fprintf(out, "## Exception Types\n\n");
+ fprintf(out, "| Exception Code | Name | Description |\n");
+ fprintf(out, "|----------------|------|-------------|\n");
+ fprintf(out, "| 0 | Instruction address misaligned | PC not aligned to 4 bytes |\n");
+ fprintf(out, "| 1 | Instruction access fault | Failed to fetch instruction |\n");
+ fprintf(out, "| 2 | Illegal instruction | Unrecognized opcode |\n");
+ fprintf(out, "| 3 | Breakpoint | EBREAK instruction executed |\n");
+ fprintf(out, "| 4 | Load address misaligned | Load address not properly aligned |\n");
+ fprintf(out, "| 5 | Load access fault | Failed to load from memory |\n");
+ fprintf(out, "| 6 | Store address misaligned | Store address not properly aligned |\n");
+ fprintf(out, "| 7 | Store access fault | Failed to store to memory |\n");
+ fprintf(out, "| 8 | Environment call from U-mode | ECALL in user mode |\n");
+ fprintf(out, "| 9 | Environment call from S-mode | ECALL in supervisor mode |\n");
+ fprintf(out, "| 11 | Environment call from M-mode | ECALL in machine mode |\n\n");
+
+ fprintf(out, "## Trap Vector\n\n");
+ fprintf(out, "On taking a trap, the implementation:\n\n");
+ fprintf(out, "1. Sets `mepc` to the PC of the trapping instruction (or next PC for ECALL/EBREAK)\n");
+ fprintf(out, "2. Sets `mcause` to the exception code\n");
+ fprintf(out, "3. Sets `mtval` to exception-specific information\n");
+ fprintf(out, "4. Sets `mstatus.MPP` to the current privilege mode\n");
+ fprintf(out, "5. Sets `mstatus.MPIE` to `mstatus.MIE`\n");
+ fprintf(out, "6. Clears `mstatus.MIE`\n");
+ fprintf(out, "7. Sets PC to `mtvec`\n\n");
+
+ fprintf(out, "\\newpage\n\n");
+}
+
+static void write_pseudo_instructions(FILE *out) {
+ fprintf(out, "# Pseudo-Instructions\n\n");
+ fprintf(out, "| Pseudo-instruction | Expansion | Description |\n");
+ fprintf(out, "|-------------------|-----------|-------------|\n");
+ fprintf(out, "| `NOP` | `ADDI x0, x0, 0` | No operation |\n");
+ fprintf(out, "| `MV rd, rs` | `ADDI rd, rs, 0` | Copy register |\n");
+ fprintf(out, "| `NOT rd, rs` | `XORI rd, rs, -1` | Bitwise NOT |\n");
+ fprintf(out, "| `NEG rd, rs` | `SUB rd, x0, rs` | Negate register |\n");
+ fprintf(out, "| `LI rd, imm` | (multiple) | Load immediate |\n");
+ fprintf(out, "| `LA rd, label` | (multiple) | Load address |\n");
+ fprintf(out, "| `RET` | `JALR x0, x1, 0` | Return from subroutine |\n");
+ fprintf(out, "| `CALL label` | (multiple) | Call subroutine |\n");
+ fprintf(out, "| `J label` | `JAL x0, label` | Unconditional jump |\n");
+ fprintf(out, "| `JR rs` | `JALR x0, rs, 0` | Jump register |\n");
+ fprintf(out, "\n\\newpage\n\n");
+}
+
+static void write_quick_reference(FILE *out) {
+ fprintf(out, "# Quick Reference\n\n");
+ fprintf(out, "## Instruction Encoding Quick Reference\n\n");
+
+ fprintf(out, "### R-Type\n\n");
+ fprintf(out, "```\n");
+ fprintf(out, " 31:27 | 26:25 | 24:20 | 19:15 | 14:12 | 11:7 | 6:0\n");
+ fprintf(out, " funct7 | — | rs2 | rs1 | funct3 | rd | opcode\n");
+ fprintf(out, " 7 | 2 | 5 | 5 | 3 | 5 | 7\n");
+ fprintf(out, "```\n\n");
+
+ fprintf(out, "### I-Type\n\n");
+ fprintf(out, "```\n");
+ fprintf(out, " 31:20 | 19:15 | 14:12 | 11:7 | 6:0\n");
+ fprintf(out, " imm[11:0]| rs1 | funct3 | rd | opcode\n");
+ fprintf(out, " 12 | 5 | 3 | 5 | 7\n");
+ fprintf(out, "```\n\n");
+
+ fprintf(out, "### S-Type\n\n");
+ fprintf(out, "```\n");
+ fprintf(out, " 31:27 | 26:25 | 24:20 | 19:15 | 14:12 | 11:7 | 6:0\n");
+ fprintf(out, " imm[11:5]| — | rs2 | rs1 | funct3 | imm[4:0]| opcode\n");
+ fprintf(out, " 7 | 2 | 5 | 5 | 3 | 5 | 7\n");
+ fprintf(out, "```\n\n");
+
+ fprintf(out, "### B-Type\n\n");
+ fprintf(out, "```\n");
+ fprintf(out, " 31 | 30:27 | 26:25 | 24:20 | 19:15 | 14:12 | 11:8 | 7 | 6:0\n");
+ fprintf(out, " imm[12]| imm[10:5]| — | rs2 | rs1 | funct3 | imm[4:1]| imm[11]| opcode\n");
+ fprintf(out, " 1 | 6 | 2 | 5 | 5 | 3 | 4 | 1 | 7\n");
+ fprintf(out, "```\n\n");
+
+ fprintf(out, "### U-Type\n\n");
+ fprintf(out, "```\n");
+ fprintf(out, " 31:12 | 11:7 | 6:0\n");
+ fprintf(out, " imm[31:12] | rd | opcode\n");
+ fprintf(out, " 20 | 5 | 7\n");
+ fprintf(out, "```\n\n");
+
+ fprintf(out, "### J-Type\n\n");
+ fprintf(out, "```\n");
+ fprintf(out, " 31 | 30:21 | 20 | 19:12 | 11:7 | 6:0\n");
+ fprintf(out, " imm[20]| imm[10:1]| imm[11]| imm[19:12]| rd | opcode\n");
+ fprintf(out, " 1 | 10 | 1 | 8 | 5 | 7\n");
+ fprintf(out, "```\n\n");
+}
+
+int gen_markdown(const IsaDb *db, const char *output_path) {
+ FILE *out = fopen(output_path, "w");
+ if (!out) {
+ fprintf(stderr, "Error: cannot write to %s\n", output_path);
+ return -1;
+ }
+
+ printf("Generating: %s\n", output_path);
+ printf(" Formats: %d\n", db->num_formats);
+ printf(" Registers: %d\n", db->num_registers);
+ printf(" Instructions: %d\n", db->num_instructions);
+ printf(" CSRs: %d\n", db->num_csrs);
+
+ write_title_page(out, db);
+ write_revision_history(out, db);
+ write_toc_placeholder(out);
+ write_intro(out, db);
+ write_registers(out, db);
+ write_formats(out, db);
+ write_instructions(out, db);
+ write_opcode_map(out, db);
+ write_exceptions(out);
+ write_pseudo_instructions(out);
+
+ if (db->num_csrs > 0)
+ write_csrs(out, db);
+
+ write_quick_reference(out);
+
+ fclose(out);
+ printf(" Done: %s\n", output_path);
+ return 0;
+}
+
+int gen_html(const IsaDb *db, const char *output_path) {
+ FILE *out = fopen(output_path, "w");
+ if (!out) {
+ fprintf(stderr, "Error: cannot write to %s\n", output_path);
+ return -1;
+ }
+
+ fprintf(out, "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n");
+ fprintf(out, "<meta charset=\"UTF-8\">\n");
+ fprintf(out, "<title>%s ISA Reference Manual</title>\n",
+ db->arch_name[0] ? db->arch_name : "Fyntv");
+ fprintf(out, "<link rel=\"stylesheet\" href=\"style.css\">\n");
+ fprintf(out, "</head>\n<body>\n<article>\n");
+
+ fprintf(out, "<header>\n");
+ fprintf(out, "<h1>%s Instruction Set Architecture</h1>\n",
+ db->arch_name[0] ? db->arch_name : "Fyntv");
+ fprintf(out, "<p class=\"subtitle\">Reference Manual</p>\n");
+ fprintf(out, "<p><strong>Version:</strong> %s &nbsp;|&nbsp; <strong>Date:</strong> %s</p>\n",
+ db->arch_version[0] ? db->arch_version : "1.0.0",
+ db->arch_date[0] ? db->arch_date : "June 2026");
+ fprintf(out, "</header>\n");
+
+ fprintf(out, "<section id=\"registers\">\n");
+ fprintf(out, "<h2>Registers</h2>\n");
+ fprintf(out, "<table><thead><tr><th>Register</th><th>ABI Name</th><th>Description</th><th>Saved</th></tr></thead><tbody>\n");
+ for (int i = 0; i < db->num_registers; i++) {
+ fprintf(out, "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n",
+ db->registers[i].name,
+ db->registers[i].abbr[0] ? db->registers[i].abbr : "—",
+ db->registers[i].desc[0] ? db->registers[i].desc : "—",
+ db->registers[i].preserve ? "Yes" : "No");
+ }
+ fprintf(out, "</tbody></table>\n</section>\n");
+
+ fprintf(out, "<section id=\"instructions\">\n");
+ fprintf(out, "<h2>Instruction Set</h2>\n");
+ fprintf(out, "<table><thead><tr><th>Mnemonic</th><th>Format</th><th>Opcode</th><th>Operands</th><th>Description</th></tr></thead><tbody>\n");
+ for (int i = 0; i < db->num_instructions; i++) {
+ fprintf(out, "<tr><td><code>%s</code></td><td>%s</td><td><code>0x%02X</code></td><td><code>%s</code></td><td>%s</td></tr>\n",
+ db->instructions[i].name,
+ db->instructions[i].fmt_name,
+ db->instructions[i].opcode,
+ db->instructions[i].operands[0] ? db->instructions[i].operands : "—",
+ db->instructions[i].desc);
+ }
+ fprintf(out, "</tbody></table>\n</section>\n");
+
+ fprintf(out, "</article>\n</body>\n</html>\n");
+ fclose(out);
+ return 0;
+}
diff --git a/gen/docs/generator.h b/gen/docs/generator.h
new file mode 100644
index 0000000..407b47a
--- /dev/null
+++ b/gen/docs/generator.h
@@ -0,0 +1,9 @@
+#ifndef GENERATOR_H
+#define GENERATOR_H
+
+#include "types.h"
+
+int gen_markdown(const IsaDb *db, const char *output_path);
+int gen_html(const IsaDb *db, const char *output_path);
+
+#endif
diff --git a/gen/docs/isa-docgen b/gen/docs/isa-docgen
new file mode 100755
index 0000000..c330db7
--- /dev/null
+++ b/gen/docs/isa-docgen
Binary files differ
diff --git a/gen/docs/isa_defs/arch.isa b/gen/docs/isa_defs/arch.isa
new file mode 100644
index 0000000..f1added
--- /dev/null
+++ b/gen/docs/isa_defs/arch.isa
@@ -0,0 +1,68 @@
+# Fyntv Architecture Definition
+ARCH Fyntv
+ NAME Fyntv
+ VERSION 0.0.0.1
+ DATE June 2026
+ STATUS Draft
+END
+
+# =============================================================================
+# Instruction Formats
+# =============================================================================
+
+FORMAT R
+ WIDTH 32
+ FIELD opcode 6:0
+ FIELD rd 11:7
+ FIELD funct3 14:12
+ FIELD rs1 19:15
+ FIELD rs2 24:20
+ FIELD funct7 31:25
+END
+
+FORMAT I
+ WIDTH 32
+ FIELD opcode 6:0
+ FIELD rd 11:7
+ FIELD funct3 14:12
+ FIELD rs1 19:15
+ FIELD imm 31:20
+END
+
+FORMAT S
+ WIDTH 32
+ FIELD opcode 6:0
+ FIELD imm_lo 11:7
+ FIELD funct3 14:12
+ FIELD rs1 19:15
+ FIELD rs2 24:20
+ FIELD imm_hi 31:25
+END
+
+FORMAT B
+ WIDTH 32
+ FIELD opcode 6:0
+ FIELD imm_4_1 11:7
+ FIELD funct3 14:12
+ FIELD rs1 19:15
+ FIELD rs2 24:20
+ FIELD imm_10_5 30:25
+ FIELD imm_12 31:31
+END
+
+FORMAT U
+ WIDTH 32
+ FIELD opcode 6:0
+ FIELD rd 11:7
+ FIELD imm 31:12
+END
+
+FORMAT J
+ WIDTH 32
+ FIELD opcode 6:0
+ FIELD rd 11:7
+ FIELD imm_19_12 19:12
+ FIELD imm_11 20:20
+ FIELD imm_10_1 30:21
+ FIELD imm_20 31:31
+END
diff --git a/gen/docs/isa_defs/csrs.isa b/gen/docs/isa_defs/csrs.isa
new file mode 100644
index 0000000..e2098ef
--- /dev/null
+++ b/gen/docs/isa_defs/csrs.isa
@@ -0,0 +1,191 @@
+# Fyntv Control and Status Register Definitions
+
+CSR mstatus
+ NUMBER 0x300
+ DESC Machine status register — holds global interrupt enable and privilege state
+END
+
+CSR misa
+ NUMBER 0x301
+ DESC Machine ISA register — encodes supported ISA extensions
+END
+
+CSR medeleg
+ NUMBER 0x302
+ DESC Machine exception delegation register
+END
+
+CSR mideleg
+ NUMBER 0x303
+ DESC Machine interrupt delegation register
+END
+
+CSR mie
+ NUMBER 0x304
+ DESC Machine interrupt-enable register
+END
+
+CSR mtvec
+ NUMBER 0x305
+ DESC Machine trap-handler base address
+END
+
+CSR mcounteren
+ NUMBER 0x306
+ DESC Machine counter enable register
+END
+
+CSR mscratch
+ NUMBER 0x340
+ DESC Scratch register for machine-mode trap handlers
+END
+
+CSR mepc
+ NUMBER 0x341
+ DESC Machine exception program counter — holds PC of trapping instruction
+END
+
+CSR mcause
+ NUMBER 0x342
+ DESC Machine trap cause — encodes exception or interrupt cause
+END
+
+CSR mtval
+ NUMBER 0x343
+ DESC Machine trap value — exception-specific information
+END
+
+CSR mip
+ NUMBER 0x344
+ DESC Machine interrupt pending register
+END
+
+CSR pmpcfg0
+ NUMBER 0x3A0
+ DESC Physical memory protection configuration 0
+END
+
+CSR pmpaddr0
+ NUMBER 0x3B0
+ DESC Physical memory protection address 0
+END
+
+CSR pmpaddr1
+ NUMBER 0x3B1
+ DESC Physical memory protection address 1
+END
+
+CSR pmpaddr2
+ NUMBER 0x3B2
+ DESC Physical memory protection address 2
+END
+
+CSR pmpaddr3
+ NUMBER 0x3B3
+ DESC Physical memory protection address 3
+END
+
+CSR mcycle
+ NUMBER 0xB00
+ DESC Machine cycle counter — counts number of clock cycles
+END
+
+CSR minstret
+ NUMBER 0xB02
+ DESC Machine instructions-retired counter
+END
+
+CSR mhpmcounter3
+ NUMBER 0xB03
+ DESC Machine hardware performance counter 3
+END
+
+CSR mhpmcounter4
+ NUMBER 0xB04
+ DESC Machine hardware performance counter 4
+END
+
+CSR mhpmcounter5
+ NUMBER 0xB05
+ DESC Machine hardware performance counter 5
+END
+
+CSR mhpmevent3
+ NUMBER 0x323
+ DESC Machine hardware performance event selector 3
+END
+
+CSR mhpmevent4
+ NUMBER 0x324
+ DESC Machine hardware performance event selector 4
+END
+
+CSR mhpmevent5
+ NUMBER 0x325
+ DESC Machine hardware performance event selector 5
+END
+
+CSR ucycle
+ NUMBER 0xC00
+ DESC User-mode cycle counter read
+END
+
+CSR uinstret
+ NUMBER 0xC02
+ DESC User-mode instructions-retired read
+END
+
+CSR ucycleh
+ NUMBER 0xC80
+ DESC Upper 32 bits of user-mode cycle counter
+END
+
+CSR uinstreth
+ NUMBER 0xC82
+ DESC Upper 32 bits of user-mode instructions-retired counter
+END
+
+CSR sstatus
+ NUMBER 0x100
+ DESC Supervisor status register
+END
+
+CSR sie
+ NUMBER 0x104
+ DESC Supervisor interrupt-enable register
+END
+
+CSR stvec
+ NUMBER 0x105
+ DESC Supervisor trap-handler base address
+END
+
+CSR sscratch
+ NUMBER 0x140
+ DESC Scratch register for supervisor-mode trap handlers
+END
+
+CSR sepc
+ NUMBER 0x141
+ DESC Supervisor exception program counter
+END
+
+CSR scause
+ NUMBER 0x142
+ DESC Supervisor trap cause register
+END
+
+CSR stval
+ NUMBER 0x143
+ DESC Supervisor trap value register
+END
+
+CSR sip
+ NUMBER 0x144
+ DESC Supervisor interrupt pending register
+END
+
+CSR satp
+ NUMBER 0x180
+ DESC Supervisor address translation and protection — controls page tables
+END
diff --git a/gen/docs/isa_defs/instructions.isa b/gen/docs/isa_defs/instructions.isa
new file mode 100644
index 0000000..c7255d3
--- /dev/null
+++ b/gen/docs/isa_defs/instructions.isa
@@ -0,0 +1,512 @@
+# Fyntv Core Integer Instructions
+
+INSTRUCTION ADD
+ FORMAT R
+ OPCODE 0x33
+ FUNCT3 0x0
+ FUNCT7 0x00
+ OPERANDS rd, rs1, rs2
+ DESC Add registers
+ NOTE rd = rs1 + rs2
+ CATEGORY Integer
+END
+
+INSTRUCTION SUB
+ FORMAT R
+ OPCODE 0x33
+ FUNCT3 0x0
+ FUNCT7 0x20
+ OPERANDS rd, rs1, rs2
+ DESC Subtract registers
+ NOTE rd = rs1 - rs2
+ CATEGORY Integer
+END
+
+INSTRUCTION ADDI
+ FORMAT I
+ OPCODE 0x13
+ FUNCT3 0x0
+ OPERANDS rd, rs1, imm12
+ DESC Add sign-extended 12-bit immediate to register rs1
+ NOTE rd = rs1 + sext(imm12)
+ CATEGORY Integer
+ IMM true
+END
+
+INSTRUCTION SLT
+ FORMAT R
+ OPCODE 0x33
+ FUNCT3 0x2
+ FUNCT7 0x00
+ OPERANDS rd, rs1, rs2
+ DESC Set if rs1 is less than rs2 (signed)
+ NOTE rd = (rs1 < rs2) ? 1 : 0
+ CATEGORY Integer
+END
+
+INSTRUCTION SLTU
+ FORMAT R
+ OPCODE 0x33
+ FUNCT3 0x3
+ FUNCT7 0x00
+ OPERANDS rd, rs1, rs2
+ DESC Set if rs1 is less than rs2 (unsigned)
+ NOTE rd = (rs1 < rs2) ? 1 : 0
+ CATEGORY Integer
+END
+
+INSTRUCTION SLTI
+ FORMAT I
+ OPCODE 0x13
+ FUNCT3 0x2
+ OPERANDS rd, rs1, imm12
+ DESC Set if rs1 is less than immediate (signed)
+ NOTE rd = (rs1 < sext(imm12)) ? 1 : 0
+ CATEGORY Integer
+ IMM true
+END
+
+INSTRUCTION SLTIU
+ FORMAT I
+ OPCODE 0x13
+ FUNCT3 0x3
+ OPERANDS rd, rs1, imm12
+ DESC Set if rs1 is less than immediate (unsigned)
+ NOTE rd = (rs1 < sext(imm12)) ? 1 : 0
+ CATEGORY Integer
+ IMM true
+END
+
+INSTRUCTION LUI
+ FORMAT U
+ OPCODE 0x37
+ OPERANDS rd, imm20
+ DESC Load upper immediate — places 20-bit immediate in upper 20 bits of rd
+ NOTE rd = imm20 << 12
+ CATEGORY Integer
+END
+
+INSTRUCTION AUIPC
+ FORMAT U
+ OPCODE 0x17
+ OPERANDS rd, imm20
+ DESC Add upper immediate to PC — forms PC-relative address
+ NOTE rd = PC + (imm20 << 12)
+ CATEGORY Integer
+END
+
+# =============================================================================
+# Logical Instructions
+# =============================================================================
+
+INSTRUCTION AND
+ FORMAT R
+ OPCODE 0x33
+ FUNCT3 0x7
+ FUNCT7 0x00
+ OPERANDS rd, rs1, rs2
+ DESC Bitwise AND
+ NOTE rd = rs1 & rs2
+ CATEGORY Logical
+END
+
+INSTRUCTION OR
+ FORMAT R
+ OPCODE 0x33
+ FUNCT3 0x6
+ FUNCT7 0x00
+ OPERANDS rd, rs1, rs2
+ DESC Bitwise OR
+ NOTE rd = rs1 | rs2
+ CATEGORY Logical
+END
+
+INSTRUCTION XOR
+ FORMAT R
+ OPCODE 0x33
+ FUNCT3 0x4
+ FUNCT7 0x00
+ OPERANDS rd, rs1, rs2
+ DESC Bitwise XOR
+ NOTE rd = rs1 ^ rs2
+ CATEGORY Logical
+END
+
+INSTRUCTION ANDI
+ FORMAT I
+ OPCODE 0x13
+ FUNCT3 0x7
+ OPERANDS rd, rs1, imm12
+ DESC Bitwise AND with immediate
+ NOTE rd = rs1 & sext(imm12)
+ CATEGORY Logical
+ IMM true
+END
+
+INSTRUCTION ORI
+ FORMAT I
+ OPCODE 0x13
+ FUNCT3 0x6
+ OPERANDS rd, rs1, imm12
+ DESC Bitwise OR with immediate
+ NOTE rd = rs1 | sext(imm12)
+ CATEGORY Logical
+ IMM true
+END
+
+INSTRUCTION XORI
+ FORMAT I
+ OPCODE 0x13
+ FUNCT3 0x4
+ OPERANDS rd, rs1, imm12
+ DESC Bitwise XOR with immediate
+ NOTE rd = rs1 ^ sext(imm12)
+ CATEGORY Logical
+ IMM true
+END
+
+# =============================================================================
+# Shift Instructions
+# =============================================================================
+
+INSTRUCTION SLL
+ FORMAT R
+ OPCODE 0x33
+ FUNCT3 0x1
+ FUNCT7 0x00
+ OPERANDS rd, rs1, rs2
+ DESC Logical left shift by lower 5 bits of rs2
+ NOTE rd = rs1 << rs2[4:0]
+ CATEGORY Shift
+END
+
+INSTRUCTION SRL
+ FORMAT R
+ OPCODE 0x33
+ FUNCT3 0x5
+ FUNCT7 0x00
+ OPERANDS rd, rs1, rs2
+ DESC Logical right shift by lower 5 bits of rs2
+ NOTE rd = rs1 >> rs2[4:0]
+ CATEGORY Shift
+END
+
+INSTRUCTION SRA
+ FORMAT R
+ OPCODE 0x33
+ FUNCT3 0x5
+ FUNCT7 0x20
+ OPERANDS rd, rs1, rs2
+ DESC Arithmetic right shift by lower 5 bits of rs2
+ NOTE rd = rs1 >>> rs2[4:0]
+ CATEGORY Shift
+END
+
+INSTRUCTION SLLI
+ FORMAT I
+ OPCODE 0x13
+ FUNCT3 0x1
+ OPERANDS rd, rs1, shamt5
+ DESC Logical left shift by immediate shift amount
+ NOTE rd = rs1 << shamt
+ CATEGORY Shift
+END
+
+INSTRUCTION SRLI
+ FORMAT I
+ OPCODE 0x13
+ FUNCT3 0x5
+ OPERANDS rd, rs1, shamt5
+ DESC Logical right shift by immediate shift amount
+ NOTE rd = rs1 >> shamt
+ CATEGORY Shift
+END
+
+INSTRUCTION SRAI
+ FORMAT I
+ OPCODE 0x13
+ FUNCT3 0x5
+ OPERANDS rd, rs1, shamt5
+ DESC Arithmetic right shift by immediate shift amount
+ NOTE rd = rs1 >>> shamt
+ CATEGORY Shift
+END
+
+# =============================================================================
+# Memory Instructions
+# =============================================================================
+
+INSTRUCTION LB
+ FORMAT I
+ OPCODE 0x03
+ FUNCT3 0x0
+ OPERANDS rd, offset(rs1)
+ DESC Load byte (sign-extended)
+ NOTE rd = sext(MEM[rs1 + offset][7:0])
+ CATEGORY Memory
+END
+
+INSTRUCTION LH
+ FORMAT I
+ OPCODE 0x03
+ FUNCT3 0x1
+ OPERANDS rd, offset(rs1)
+ DESC Load halfword (sign-extended)
+ NOTE rd = sext(MEM[rs1 + offset][15:0])
+ CATEGORY Memory
+END
+
+INSTRUCTION LW
+ FORMAT I
+ OPCODE 0x03
+ FUNCT3 0x2
+ OPERANDS rd, offset(rs1)
+ DESC Load word
+ NOTE rd = MEM[rs1 + offset][31:0]
+ CATEGORY Memory
+END
+
+INSTRUCTION LBU
+ FORMAT I
+ OPCODE 0x03
+ FUNCT3 0x4
+ OPERANDS rd, offset(rs1)
+ DESC Load byte (zero-extended)
+ NOTE rd = MEM[rs1 + offset][7:0]
+ CATEGORY Memory
+END
+
+INSTRUCTION LHU
+ FORMAT I
+ OPCODE 0x03
+ FUNCT3 0x5
+ OPERANDS rd, offset(rs1)
+ DESC Load halfword (zero-extended)
+ NOTE rd = MEM[rs1 + offset][15:0]
+ CATEGORY Memory
+END
+
+INSTRUCTION SB
+ FORMAT S
+ OPCODE 0x23
+ FUNCT3 0x0
+ OPERANDS rs2, offset(rs1)
+ DESC Store byte
+ NOTE MEM[rs1 + offset][7:0] = rs2[7:0]
+ CATEGORY Memory
+END
+
+INSTRUCTION SH
+ FORMAT S
+ OPCODE 0x23
+ FUNCT3 0x1
+ OPERANDS rs2, offset(rs1)
+ DESC Store halfword
+ NOTE MEM[rs1 + offset][15:0] = rs2[15:0]
+ CATEGORY Memory
+END
+
+INSTRUCTION SW
+ FORMAT S
+ OPCODE 0x23
+ FUNCT3 0x2
+ OPERANDS rs2, offset(rs1)
+ DESC Store word
+ NOTE MEM[rs1 + offset][31:0] = rs2[31:0]
+ CATEGORY Memory
+END
+
+# =============================================================================
+# Branch Instructions
+# =============================================================================
+
+INSTRUCTION BEQ
+ FORMAT B
+ OPCODE 0x63
+ FUNCT3 0x0
+ OPERANDS rs1, rs2, label
+ DESC Branch equal
+ NOTE if (rs1 == rs2) PC += sext(offset)
+ CATEGORY Branch
+END
+
+INSTRUCTION BNE
+ FORMAT B
+ OPCODE 0x63
+ FUNCT3 0x1
+ OPERANDS rs1, rs2, label
+ DESC Branch not equal
+ NOTE if (rs1 != rs2) PC += sext(offset)
+ CATEGORY Branch
+END
+
+INSTRUCTION BLT
+ FORMAT B
+ OPCODE 0x63
+ FUNCT3 0x4
+ OPERANDS rs1, rs2, label
+ DESC Branch less than (signed)
+ NOTE if (rs1 < rs2) PC += sext(offset)
+ CATEGORY Branch
+END
+
+INSTRUCTION BGE
+ FORMAT B
+ OPCODE 0x63
+ FUNCT3 0x5
+ OPERANDS rs1, rs2, label
+ DESC Branch greater than or equal (signed)
+ NOTE if (rs1 >= rs2) PC += sext(offset)
+ CATEGORY Branch
+END
+
+INSTRUCTION BLTU
+ FORMAT B
+ OPCODE 0x63
+ FUNCT3 0x6
+ OPERANDS rs1, rs2, label
+ DESC Branch less than (unsigned)
+ NOTE if (rs1 < rs2) PC += sext(offset)
+ CATEGORY Branch
+END
+
+INSTRUCTION BGEU
+ FORMAT B
+ OPCODE 0x63
+ FUNCT3 0x7
+ OPERANDS rs1, rs2, label
+ DESC Branch greater than or equal (unsigned)
+ NOTE if (rs1 >= rs2) PC += sext(offset)
+ CATEGORY Branch
+END
+
+# =============================================================================
+# Jump Instructions
+# =============================================================================
+
+INSTRUCTION JAL
+ FORMAT J
+ OPCODE 0x6F
+ OPERANDS rd, label
+ DESC Jump and link — jump to PC+offset, save return address to rd
+ NOTE rd = PC + 4; PC += sext(offset)
+ CATEGORY Jump
+END
+
+INSTRUCTION JALR
+ FORMAT I
+ OPCODE 0x67
+ FUNCT3 0x0
+ OPERANDS rd, rs1, offset
+ DESC Jump and link register — jump to rs1+offset, save return address
+ NOTE rd = PC + 4; PC = (rs1 + sext(offset)) & ~1
+ CATEGORY Jump
+END
+
+# =============================================================================
+# Synchronization Instructions
+# =============================================================================
+
+INSTRUCTION FENCE
+ FORMAT I
+ OPCODE 0x0F
+ FUNCT3 0x0
+ OPERANDS pred, succ
+ DESC Memory ordering fence
+ NOTE Orders memory accesses as specified by pred and succ fields
+ CATEGORY Synchronization
+END
+
+INSTRUCTION FENCEI
+ FORMAT I
+ OPCODE 0x0F
+ FUNCT3 0x1
+ OPERANDS —
+ DESC Instruction fence — synchronizes instruction and data streams
+ NOTE Flushes instruction cache after data writes
+ CATEGORY Synchronization
+END
+
+# =============================================================================
+# System Instructions
+# =============================================================================
+
+INSTRUCTION ECALL
+ FORMAT I
+ OPCODE 0x73
+ FUNCT3 0x0
+ OPERANDS —
+ DESC Environment call — raises a system call exception
+ NOTE Traps to the configured exception handler
+ CATEGORY System
+END
+
+INSTRUCTION EBREAK
+ FORMAT I
+ OPCODE 0x73
+ FUNCT3 0x0
+ OPERANDS —
+ DESC Breakpoint — raises a breakpoint exception
+ NOTE Used by debuggers to halt program execution
+ CATEGORY System
+END
+
+INSTRUCTION CSRRW
+ FORMAT I
+ OPCODE 0x73
+ FUNCT3 0x1
+ OPERANDS rd, csr, rs1
+ DESC Atomic read/write CSR — writes rs1 to CSR, returns old value in rd
+ NOTE rd = CSR[csr]; CSR[csr] = rs1
+ CATEGORY System
+END
+
+INSTRUCTION CSRRS
+ FORMAT I
+ OPCODE 0x73
+ FUNCT3 0x2
+ OPERANDS rd, csr, rs1
+ DESC Atomic read and set bits in CSR
+ NOTE rd = CSR[csr]; CSR[csr] |= rs1
+ CATEGORY System
+END
+
+INSTRUCTION CSRRC
+ FORMAT I
+ OPCODE 0x73
+ FUNCT3 0x3
+ OPERANDS rd, csr, rs1
+ DESC Atomic read and clear bits in CSR
+ NOTE rd = CSR[csr]; CSR[csr] &= ~rs1
+ CATEGORY System
+END
+
+INSTRUCTION CSRRWI
+ FORMAT I
+ OPCODE 0x73
+ FUNCT3 0x5
+ OPERANDS rd, csr, uimm5
+ DESC Atomic read/write CSR with immediate
+ NOTE rd = CSR[csr]; CSR[csr] = zimm
+ CATEGORY System
+END
+
+INSTRUCTION CSRRSI
+ FORMAT I
+ OPCODE 0x73
+ FUNCT3 0x6
+ OPERANDS rd, csr, uimm5
+ DESC Atomic read and set bits in CSR with immediate
+ NOTE rd = CSR[csr]; CSR[csr] |= zimm
+ CATEGORY System
+END
+
+INSTRUCTION CSRRCI
+ FORMAT I
+ OPCODE 0x73
+ FUNCT3 0x7
+ OPERANDS rd, csr, uimm5
+ DESC Atomic read and clear bits in CSR with immediate
+ NOTE rd = CSR[csr]; CSR[csr] &= ~zimm
+ CATEGORY System
+END
diff --git a/gen/docs/isa_defs/registers.isa b/gen/docs/isa_defs/registers.isa
new file mode 100644
index 0000000..baa8124
--- /dev/null
+++ b/gen/docs/isa_defs/registers.isa
@@ -0,0 +1,289 @@
+# Fyntv Register Definitions
+
+REGISTER x0
+ ABBR zero
+ DESC Always-zero register — reads return 0, writes are discarded
+ PRESERVE false
+ CALLER false
+ ARG false
+ INDEX 0
+END
+
+REGISTER x1
+ ABBR ra
+ DESC Return address link register
+ PRESERVE false
+ CALLER false
+ ARG false
+ INDEX 1
+END
+
+REGISTER x2
+ ABBR sp
+ DESC Stack pointer
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 2
+END
+
+REGISTER x3
+ ABBR gp
+ DESC Global pointer
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 3
+END
+
+REGISTER x4
+ ABBR tp
+ DESC Thread pointer
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 4
+END
+
+REGISTER x5
+ ABBR t0
+ DESC Temporary register 0
+ PRESERVE false
+ CALLER true
+ ARG false
+ INDEX 5
+END
+
+REGISTER x6
+ ABBR t1
+ DESC Temporary register 1
+ PRESERVE false
+ CALLER true
+ ARG false
+ INDEX 6
+END
+
+REGISTER x7
+ ABBR t2
+ DESC Temporary register 2
+ PRESERVE false
+ CALLER true
+ ARG false
+ INDEX 7
+END
+
+REGISTER x8
+ ABBR s0
+ DESC Saved register 0 / frame pointer
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 8
+END
+
+REGISTER x9
+ ABBR s1
+ DESC Saved register 1
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 9
+END
+
+REGISTER x10
+ ABBR a0
+ DESC Function argument 0 / return value 0
+ PRESERVE false
+ CALLER true
+ ARG true
+ INDEX 10
+END
+
+REGISTER x11
+ ABBR a1
+ DESC Function argument 1 / return value 1
+ PRESERVE false
+ CALLER true
+ ARG true
+ INDEX 11
+END
+
+REGISTER x12
+ ABBR a2
+ DESC Function argument 2
+ PRESERVE false
+ CALLER true
+ ARG true
+ INDEX 12
+END
+
+REGISTER x13
+ ABBR a3
+ DESC Function argument 3
+ PRESERVE false
+ CALLER true
+ ARG true
+ INDEX 13
+END
+
+REGISTER x14
+ ABBR a4
+ DESC Function argument 4
+ PRESERVE false
+ CALLER true
+ ARG true
+ INDEX 14
+END
+
+REGISTER x15
+ ABBR a5
+ DESC Function argument 5
+ PRESERVE false
+ CALLER true
+ ARG true
+ INDEX 15
+END
+
+REGISTER x16
+ ABBR a6
+ DESC Function argument 6
+ PRESERVE false
+ CALLER true
+ ARG true
+ INDEX 16
+END
+
+REGISTER x17
+ ABBR a7
+ DESC Function argument 7
+ PRESERVE false
+ CALLER true
+ ARG true
+ INDEX 17
+END
+
+REGISTER x18
+ ABBR s2
+ DESC Saved register 2
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 18
+END
+
+REGISTER x19
+ ABBR s3
+ DESC Saved register 3
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 19
+END
+
+REGISTER x20
+ ABBR s4
+ DESC Saved register 4
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 20
+END
+
+REGISTER x21
+ ABBR s5
+ DESC Saved register 5
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 21
+END
+
+REGISTER x22
+ ABBR s6
+ DESC Saved register 6
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 22
+END
+
+REGISTER x23
+ ABBR s7
+ DESC Saved register 7
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 23
+END
+
+REGISTER x24
+ ABBR s8
+ DESC Saved register 8
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 24
+END
+
+REGISTER x25
+ ABBR s9
+ DESC Saved register 9
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 25
+END
+
+REGISTER x26
+ ABBR s10
+ DESC Saved register 10
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 26
+END
+
+REGISTER x27
+ ABBR s11
+ DESC Saved register 11
+ PRESERVE true
+ CALLER false
+ ARG false
+ INDEX 27
+END
+
+REGISTER x28
+ ABBR t3
+ DESC Temporary register 3
+ PRESERVE false
+ CALLER true
+ ARG false
+ INDEX 28
+END
+
+REGISTER x29
+ ABBR t4
+ DESC Temporary register 4
+ PRESERVE false
+ CALLER true
+ ARG false
+ INDEX 29
+END
+
+REGISTER x30
+ ABBR t5
+ DESC Temporary register 5
+ PRESERVE false
+ CALLER true
+ ARG false
+ INDEX 30
+END
+
+REGISTER x31
+ ABBR t6
+ DESC Temporary register 6
+ PRESERVE false
+ CALLER true
+ ARG false
+ INDEX 31
+END
diff --git a/gen/docs/main.c b/gen/docs/main.c
new file mode 100644
index 0000000..760253e
--- /dev/null
+++ b/gen/docs/main.c
@@ -0,0 +1,83 @@
+#include "parser.h"
+#include "generator.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void print_usage(const char *prog) {
+ fprintf(stderr, "Usage: %s [options] <isa_defs_dir>\n\n", prog);
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -o <file> Output Markdown file (default: doc/spec/isa_reference.md)\n");
+ fprintf(stderr, " --html Also generate HTML output\n");
+ fprintf(stderr, " --help Show this help\n\n");
+ fprintf(stderr, "Generates professional ISA reference documentation from .isa definition files.\n");
+}
+
+int main(int argc, char **argv) {
+ const char *defs_dir = NULL;
+ const char *output = "doc/spec/isa_reference.md";
+ int gen_html_flag = 0;
+
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "--help") == 0) {
+ print_usage(argv[0]);
+ return 0;
+ } else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) {
+ output = argv[++i];
+ } else if (strcmp(argv[i], "--html") == 0) {
+ gen_html_flag = 1;
+ } else if (argv[i][0] != '-') {
+ defs_dir = argv[i];
+ } else {
+ fprintf(stderr, "Unknown option: %s\n", argv[i]);
+ print_usage(argv[0]);
+ return 1;
+ }
+ }
+
+ if (!defs_dir) {
+ fprintf(stderr, "Error: no ISA definitions directory specified.\n\n");
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ IsaDb db;
+ memset(&db, 0, sizeof(db));
+
+ printf("ISA Documentation Generator\n");
+ printf("Reading definitions from: %s\n", defs_dir);
+
+ if (isa_parse_dir(defs_dir, &db) != 0) {
+ fprintf(stderr, "Error: failed to parse ISA definitions.\n");
+ return 1;
+ }
+
+ printf("\nParsed:\n");
+ printf(" %d format(s)\n", db.num_formats);
+ printf(" %d register(s)\n", db.num_registers);
+ printf(" %d instruction(s)\n", db.num_instructions);
+ printf(" %d CSR(s)\n", db.num_csrs);
+
+ if (db.num_instructions == 0) {
+ fprintf(stderr, "Warning: no instructions loaded. Output will be sparse.\n");
+ }
+
+ if (gen_markdown(&db, output) != 0) {
+ fprintf(stderr, "Error: failed to generate Markdown.\n");
+ return 1;
+ }
+
+ if (gen_html_flag) {
+ char html_path[1024];
+ snprintf(html_path, sizeof(html_path), "%s.html", output);
+ const char *ext = strrchr(output, '.');
+ if (ext) {
+ size_t len = ext - output;
+ snprintf(html_path, sizeof(html_path), "%.*s.html", (int)len, output);
+ }
+ gen_html(&db, html_path);
+ }
+
+ printf("\nDone. Output: %s\n", output);
+ return 0;
+}
diff --git a/gen/docs/parser.c b/gen/docs/parser.c
new file mode 100644
index 0000000..2852070
--- /dev/null
+++ b/gen/docs/parser.c
@@ -0,0 +1,256 @@
+#include "parser.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+
+static void str_trim(char *s) {
+ char *e;
+ while (isspace((unsigned char)*s)) s++;
+ if (*s == 0) return;
+ e = s + strlen(s) - 1;
+ while (e > s && isspace((unsigned char)*e)) e--;
+ *(e + 1) = '\0';
+}
+
+static int str_split(const char *line, char **key, char **val) {
+ static char buf[MAX_LINE];
+ strncpy(buf, line, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ char *eq = strchr(buf, ' ');
+ if (!eq) { *key = buf; *val = ""; return 0; }
+ *eq = '\0';
+ *key = buf;
+ *val = eq + 1;
+ str_trim(*key);
+ str_trim(*val);
+ return 1;
+}
+
+static int str_to_int(const char *s) {
+ if (strncmp(s, "0x", 2) == 0 || strncmp(s, "0X", 2) == 0)
+ return (int)strtol(s, NULL, 16);
+ return atoi(s);
+}
+
+static int str_to_bool(const char *s) {
+ return strcmp(s, "true") == 0 || strcmp(s, "yes") == 0 || strcmp(s, "1") == 0;
+}
+
+static int parse_block(FILE *f, const char *block_type, const char *block_name, IsaDb *db) {
+ char line[MAX_LINE];
+
+ if (strcasecmp(block_type, "FORMAT") == 0) {
+ if (db->num_formats >= MAX_FMTS) {
+ fprintf(stderr, "Too many formats (max %d)\n", MAX_FMTS);
+ return -1;
+ }
+ IsaFormat *fmt = &db->formats[db->num_formats];
+ strncpy(fmt->name, block_name, MAX_NAME - 1);
+ fmt->width = 32;
+ fmt->num_fields = 0;
+
+ while (fgets(line, sizeof(line), f)) {
+ char *trimmed = line;
+ while (isspace((unsigned char)*trimmed)) trimmed++;
+ if (*trimmed == '#' || *trimmed == '\n') continue;
+ if (strncmp(trimmed, "END", 3) == 0) break;
+
+ char *key, *val;
+ str_split(trimmed, &key, &val);
+
+ if (strcasecmp(key, "WIDTH") == 0) {
+ fmt->width = str_to_int(val);
+ } else if (strcasecmp(key, "FIELD") == 0) {
+ if (fmt->num_fields >= MAX_FIELDS) {
+ fprintf(stderr, "Too many fields in format %s\n", fmt->name);
+ continue;
+ }
+ char fname[64];
+ int high, low;
+ if (sscanf(val, "%63s %d:%d", fname, &high, &low) >= 3) {
+ strncpy(fmt->fields[fmt->num_fields].name, fname, MAX_LABEL - 1);
+ fmt->fields[fmt->num_fields].high = high;
+ fmt->fields[fmt->num_fields].low = low;
+ fmt->num_fields++;
+ }
+ }
+ }
+ db->num_formats++;
+ } else if (strcasecmp(block_type, "REGISTER") == 0) {
+ if (db->num_registers >= MAX_REGS) {
+ fprintf(stderr, "Too many registers (max %d)\n", MAX_REGS);
+ return -1;
+ }
+ IsaRegister *reg = &db->registers[db->num_registers];
+ memset(reg, 0, sizeof(*reg));
+ strncpy(reg->name, block_name, MAX_NAME - 1);
+ reg->preserve = -1;
+
+ while (fgets(line, sizeof(line), f)) {
+ char *trimmed = line;
+ while (isspace((unsigned char)*trimmed)) trimmed++;
+ if (*trimmed == '#' || *trimmed == '\n') continue;
+ if (strncmp(trimmed, "END", 3) == 0) break;
+
+ char *key, *val;
+ str_split(trimmed, &key, &val);
+
+ if (strcasecmp(key, "ABBR") == 0)
+ strncpy(reg->abbr, val, MAX_LABEL - 1);
+ else if (strcasecmp(key, "DESC") == 0)
+ strncpy(reg->desc, val, MAX_DESC - 1);
+ else if (strcasecmp(key, "PRESERVE") == 0)
+ reg->preserve = str_to_bool(val);
+ else if (strcasecmp(key, "CALLER") == 0)
+ reg->caller_saved = str_to_bool(val);
+ else if (strcasecmp(key, "ARG") == 0)
+ reg->arg_reg = str_to_bool(val);
+ else if (strcasecmp(key, "INDEX") == 0)
+ reg->index = str_to_int(val);
+ }
+ if (reg->preserve == -1) reg->preserve = 0;
+ db->num_registers++;
+ } else if (strcasecmp(block_type, "INSTRUCTION") == 0) {
+ if (db->num_instructions >= MAX_INSTS) {
+ fprintf(stderr, "Too many instructions (max %d)\n", MAX_INSTS);
+ return -1;
+ }
+ IsaInstruction *inst = &db->instructions[db->num_instructions];
+ memset(inst, 0, sizeof(*inst));
+ strncpy(inst->name, block_name, MAX_NAME - 1);
+ inst->funct3_valid = 0;
+ inst->funct7_valid = 0;
+
+ while (fgets(line, sizeof(line), f)) {
+ char *trimmed = line;
+ while (isspace((unsigned char)*trimmed)) trimmed++;
+ if (*trimmed == '#' || *trimmed == '\n') continue;
+ if (strncmp(trimmed, "END", 3) == 0) break;
+
+ char *key, *val;
+ str_split(trimmed, &key, &val);
+
+ if (strcasecmp(key, "FORMAT") == 0)
+ strncpy(inst->fmt_name, val, MAX_NAME - 1);
+ else if (strcasecmp(key, "OPCODE") == 0)
+ inst->opcode = (uint32_t)str_to_int(val);
+ else if (strcasecmp(key, "FUNCT3") == 0) {
+ inst->funct3 = (uint32_t)str_to_int(val);
+ inst->funct3_valid = 1;
+ } else if (strcasecmp(key, "FUNCT7") == 0) {
+ inst->funct7 = (uint32_t)str_to_int(val);
+ inst->funct7_valid = 1;
+ } else if (strcasecmp(key, "OPERANDS") == 0)
+ strncpy(inst->operands, val, MAX_OPERANDS - 1);
+ else if (strcasecmp(key, "DESC") == 0)
+ strncpy(inst->desc, val, MAX_DESC - 1);
+ else if (strcasecmp(key, "NOTE") == 0)
+ strncpy(inst->note, val, MAX_NOTE - 1);
+ else if (strcasecmp(key, "CATEGORY") == 0)
+ strncpy(inst->category, val, MAX_LABEL - 1);
+ else if (strcasecmp(key, "IMM") == 0)
+ inst->has_imm = str_to_bool(val);
+ }
+ db->num_instructions++;
+ } else if (strcasecmp(block_type, "CSR") == 0) {
+ if (db->num_csrs >= MAX_CSRS) {
+ fprintf(stderr, "Too many CSRs (max %d)\n", MAX_CSRS);
+ return -1;
+ }
+ IsaCsr *csr = &db->csrs[db->num_csrs];
+ memset(csr, 0, sizeof(*csr));
+ strncpy(csr->name, block_name, MAX_NAME - 1);
+
+ while (fgets(line, sizeof(line), f)) {
+ char *trimmed = line;
+ while (isspace((unsigned char)*trimmed)) trimmed++;
+ if (*trimmed == '#' || *trimmed == '\n') continue;
+ if (strncmp(trimmed, "END", 3) == 0) break;
+
+ char *key, *val;
+ str_split(trimmed, &key, &val);
+
+ if (strcasecmp(key, "NUMBER") == 0)
+ csr->number = str_to_int(val);
+ else if (strcasecmp(key, "DESC") == 0)
+ strncpy(csr->desc, val, MAX_DESC - 1);
+ }
+ db->num_csrs++;
+ } else if (strcasecmp(block_type, "ARCH") == 0) {
+ while (fgets(line, sizeof(line), f)) {
+ char *trimmed = line;
+ while (isspace((unsigned char)*trimmed)) trimmed++;
+ if (*trimmed == '#' || *trimmed == '\n') continue;
+ if (strncmp(trimmed, "END", 3) == 0) break;
+
+ char *key, *val;
+ str_split(trimmed, &key, &val);
+
+ if (strcasecmp(key, "NAME") == 0)
+ strncpy(db->arch_name, val, MAX_NAME - 1);
+ else if (strcasecmp(key, "VERSION") == 0)
+ strncpy(db->arch_version, val, MAX_LABEL - 1);
+ else if (strcasecmp(key, "DATE") == 0)
+ strncpy(db->arch_date, val, MAX_LABEL - 1);
+ else if (strcasecmp(key, "STATUS") == 0)
+ strncpy(db->arch_status, val, MAX_LABEL - 1);
+ }
+ }
+
+ return 0;
+}
+
+int isa_parse_file(const char *path, IsaDb *db) {
+ FILE *f = fopen(path, "r");
+ if (!f) {
+ fprintf(stderr, "Error: cannot open %s\n", path);
+ return -1;
+ }
+
+ char line[MAX_LINE];
+ int line_num = 0;
+
+ while (fgets(line, sizeof(line), f)) {
+ line_num++;
+ char *trimmed = line;
+ while (isspace((unsigned char)*trimmed)) trimmed++;
+ if (*trimmed == '#' || *trimmed == '\n' || *trimmed == '\r') continue;
+
+ char type[MAX_LABEL], name[MAX_NAME];
+ if (sscanf(trimmed, "%31s %63s", type, name) >= 2) {
+ if (parse_block(f, type, name, db) != 0) {
+ fprintf(stderr, "Error parsing %s in %s line %d\n", type, path, line_num);
+ fclose(f);
+ return -1;
+ }
+ }
+ }
+
+ fclose(f);
+ return 0;
+}
+
+int isa_parse_dir(const char *dir, IsaDb *db) {
+ DIR *d = opendir(dir);
+ if (!d) {
+ fprintf(stderr, "Error: cannot open directory %s\n", dir);
+ return -1;
+ }
+
+ struct dirent *entry;
+ while ((entry = readdir(d)) != NULL) {
+ if (entry->d_type != DT_REG) continue;
+ const char *ext = strrchr(entry->d_name, '.');
+ if (!ext || strcasecmp(ext, ".isa") != 0) continue;
+
+ char path[MAX_PATH];
+ snprintf(path, sizeof(path), "%s/%s", dir, entry->d_name);
+ printf(" Parsing: %s\n", path);
+ isa_parse_file(path, db);
+ }
+
+ closedir(d);
+ return 0;
+}
diff --git a/gen/docs/parser.h b/gen/docs/parser.h
new file mode 100644
index 0000000..53a27fc
--- /dev/null
+++ b/gen/docs/parser.h
@@ -0,0 +1,9 @@
+#ifndef PARSER_H
+#define PARSER_H
+
+#include "types.h"
+
+int isa_parse_file(const char *path, IsaDb *db);
+int isa_parse_dir(const char *dir, IsaDb *db);
+
+#endif
diff --git a/gen/docs/style.css b/gen/docs/style.css
new file mode 100644
index 0000000..305b00b
--- /dev/null
+++ b/gen/docs/style.css
@@ -0,0 +1,139 @@
+/* style.css — Professional styling for ISA reference HTML (used by wkhtmltopdf) */
+
+body {
+ font-family: "Times New Roman", Times, serif;
+ font-size: 11pt;
+ line-height: 1.5;
+ color: #1a1a1a;
+ max-width: 7in;
+ margin: 0 auto;
+ padding: 1in 0.5in;
+}
+
+h1 {
+ font-size: 24pt;
+ text-align: center;
+ margin-top: 2in;
+ margin-bottom: 0.3in;
+ page-break-before: always;
+}
+
+h1:first-of-type {
+ margin-top: 3in;
+ page-break-before: avoid;
+}
+
+.subtitle {
+ text-align: center;
+ font-size: 16pt;
+ color: #555;
+ margin-bottom: 0.5in;
+}
+
+h2 {
+ font-size: 16pt;
+ border-bottom: 2px solid #333;
+ padding-bottom: 4pt;
+ margin-top: 0.5in;
+}
+
+h3 {
+ font-size: 13pt;
+ margin-top: 0.3in;
+}
+
+h4 {
+ font-size: 11pt;
+ font-style: italic;
+ margin-top: 0.2in;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 0.2in 0;
+ font-size: 9.5pt;
+}
+
+th, td {
+ border: 1px solid #999;
+ padding: 4pt 6pt;
+ text-align: left;
+ vertical-align: top;
+}
+
+th {
+ background-color: #e8e8e8;
+ font-weight: bold;
+}
+
+tr:nth-child(even) {
+ background-color: #f6f6f6;
+}
+
+code {
+ font-family: "Courier New", Courier, monospace;
+ font-size: 9.5pt;
+ background-color: #f0f0f0;
+ padding: 1pt 3pt;
+ border-radius: 2pt;
+}
+
+pre {
+ font-family: "Courier New", Courier, monospace;
+ font-size: 9pt;
+ background-color: #f8f8f8;
+ border: 1px solid #ddd;
+ padding: 8pt;
+ overflow-x: auto;
+ line-height: 1.3;
+}
+
+pre code {
+ background: none;
+ padding: 0;
+}
+
+/* Page breaks */
+section, .chapter {
+ page-break-before: always;
+}
+
+/* Tables of contents */
+#TOC {
+ page-break-after: always;
+}
+
+#TOC ul {
+ list-style: none;
+ padding-left: 0;
+}
+
+#TOC li {
+ margin: 4pt 0;
+}
+
+/* Header and footer */
+@page {
+ @top-center {
+ content: element(header);
+ font-size: 9pt;
+ color: #888;
+ }
+ @bottom-center {
+ content: counter(page);
+ font-size: 9pt;
+ }
+}
+
+@media print {
+ body {
+ padding: 0;
+ }
+ table {
+ page-break-inside: avoid;
+ }
+ h2, h3 {
+ page-break-after: avoid;
+ }
+}
diff --git a/gen/docs/types.h b/gen/docs/types.h
new file mode 100644
index 0000000..14fb4e0
--- /dev/null
+++ b/gen/docs/types.h
@@ -0,0 +1,78 @@
+#ifndef TYPES_H
+#define TYPES_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define MAX_NAME 64
+#define MAX_LABEL 32
+#define MAX_DESC 512
+#define MAX_NOTE 512
+#define MAX_OPERANDS 128
+#define MAX_FIELDS 8
+#define MAX_INSTS 256
+#define MAX_REGS 64
+#define MAX_FMTS 16
+#define MAX_CSRS 128
+#define MAX_LINE 1024
+#define MAX_TOKENS 16
+#define MAX_PATH 256
+
+typedef struct {
+ char name[MAX_NAME];
+ int width;
+ int num_fields;
+ struct {
+ char name[MAX_LABEL];
+ int high;
+ int low;
+ } fields[MAX_FIELDS];
+} IsaFormat;
+
+typedef struct {
+ char name[MAX_NAME];
+ char abbr[MAX_LABEL];
+ char desc[MAX_DESC];
+ int preserve;
+ int caller_saved;
+ int arg_reg;
+ int index;
+} IsaRegister;
+
+typedef struct {
+ char name[MAX_NAME];
+ char fmt_name[MAX_NAME];
+ uint32_t opcode;
+ uint32_t funct3;
+ uint32_t funct7;
+ uint32_t funct3_valid;
+ uint32_t funct7_valid;
+ char operands[MAX_OPERANDS];
+ char desc[MAX_DESC];
+ char note[MAX_NOTE];
+ char category[MAX_LABEL];
+ int has_imm;
+} IsaInstruction;
+
+typedef struct {
+ char name[MAX_NAME];
+ int number;
+ char desc[MAX_DESC];
+} IsaCsr;
+
+typedef struct {
+ IsaFormat formats[MAX_FMTS];
+ int num_formats;
+ IsaRegister registers[MAX_REGS];
+ int num_registers;
+ IsaInstruction instructions[MAX_INSTS];
+ int num_instructions;
+ IsaCsr csrs[MAX_CSRS];
+ int num_csrs;
+ char arch_name[MAX_NAME];
+ char arch_version[MAX_LABEL];
+ char arch_date[MAX_LABEL];
+ char arch_status[MAX_LABEL];
+} IsaDb;
+
+#endif