summaryrefslogtreecommitdiff
path: root/gen/docs/generator.c
diff options
context:
space:
mode:
Diffstat (limited to 'gen/docs/generator.c')
-rw-r--r--gen/docs/generator.c481
1 files changed, 481 insertions, 0 deletions
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;
+}