summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Scheibe <mail@robert-scheibe.de>2026-01-17 16:06:30 +0100
committerRobert Scheibe <mail@robert-scheibe.de>2026-01-17 16:06:30 +0100
commit75ff4c5f1204b34eff30d4e0ee77fce6d386505f (patch)
tree18ec3604cf3fcaed67fe9996b3adf156bd4c3c92
parentc6c9104a61bb609b040c592ee54ece1ac4b36b0c (diff)
added Makefile
-rwxr-xr-xhp41_program_to_pdf.py182
-rw-r--r--hp41cx-emulator/Makefile97
2 files changed, 279 insertions, 0 deletions
diff --git a/hp41_program_to_pdf.py b/hp41_program_to_pdf.py
new file mode 100755
index 0000000..630a5b6
--- /dev/null
+++ b/hp41_program_to_pdf.py
@@ -0,0 +1,182 @@
+#!/usr/bin/env python3
+"""
+Generate a PDF listing for an HP-41C program with line numbers and a header.
+
+Requires: reportlab (pip install reportlab)
+"""
+
+from __future__ import annotations
+
+import argparse
+import os
+import sys
+from datetime import datetime
+import re
+
+
+def require_reportlab():
+ try:
+ from reportlab.lib.pagesizes import A4 # noqa: F401
+ from reportlab.lib.units import mm # noqa: F401
+ from reportlab.pdfgen import canvas # noqa: F401
+ except Exception as exc: # pragma: no cover
+ print("Error: reportlab is required. Install via: pip install reportlab", file=sys.stderr)
+ raise SystemExit(2) from exc
+
+
+def iter_wrapped_lines(text: str, max_width: float, font_name: str, font_size: int):
+ from reportlab.pdfbase import pdfmetrics
+
+ if not text:
+ return [""]
+
+ words = text.split(" ")
+ lines = []
+ current = ""
+ for word in words:
+ candidate = word if not current else f"{current} {word}"
+ width = pdfmetrics.stringWidth(candidate, font_name, font_size)
+ if width <= max_width:
+ current = candidate
+ else:
+ if current:
+ lines.append(current)
+ current = word
+ if current:
+ lines.append(current)
+ return lines
+
+
+def generate_pdf(lines: list[str], output_path: str, program_name: str):
+ from reportlab.lib.pagesizes import A4
+ from reportlab.lib.units import mm
+ from reportlab.pdfgen import canvas
+ from reportlab.pdfbase import pdfmetrics
+
+ page_width, page_height = A4
+ margin_left = 18 * mm
+ margin_right = 18 * mm
+ margin_top = 18 * mm
+ margin_bottom = 18 * mm
+
+ header_font = "Helvetica-Bold"
+ body_font = "Courier"
+ header_size = 14
+ body_size = 10
+ line_height = 1.35 * body_size
+
+ content_width = page_width - margin_left - margin_right
+ usable_height = page_height - margin_top - margin_bottom - (header_size + 6)
+ lines_per_page = int(usable_height // line_height)
+ if lines_per_page <= 0:
+ raise ValueError("Page layout too tight. Increase margins or reduce font size.")
+
+ c = canvas.Canvas(output_path, pagesize=A4)
+ c.setAuthor("hp41_program_to_pdf.py")
+ c.setTitle(program_name)
+
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
+ total_lines = len(lines)
+ line_number = 1
+ page_number = 1
+
+ def draw_header():
+ c.setFont(header_font, header_size)
+ c.drawString(margin_left, page_height - margin_top, program_name)
+ c.setFont("Helvetica", 8)
+ c.drawRightString(page_width - margin_right, page_height - margin_top + 2, timestamp)
+ c.drawRightString(page_width - margin_right, page_height - margin_top - 10, f"Page {page_number}")
+ c.line(margin_left, page_height - margin_top - 14, page_width - margin_right, page_height - margin_top - 14)
+
+ y_start = page_height - margin_top - 24
+ y = y_start
+
+ draw_header()
+ c.setFont(body_font, body_size)
+
+ # Pre-parse line numbers when present; keep blank/comment lines but don't
+ # count them for numbering.
+ parsed_lines: list[tuple[int | None, str]] = []
+ auto_line_no = 1
+ number_re = re.compile(r"^\s*(\d+)[\s:]+(.*)$")
+ for original in lines:
+ stripped = original.strip()
+ if stripped == "" or stripped.startswith("#"):
+ parsed_lines.append((None, original))
+ continue
+ match = number_re.match(original)
+ if match:
+ num = int(match.group(1))
+ text = match.group(2).rstrip()
+ parsed_lines.append((num, text))
+ auto_line_no = num + 1
+ else:
+ parsed_lines.append((auto_line_no, original))
+ auto_line_no += 1
+
+ numbered = [n for n, _ in parsed_lines if n is not None]
+ if numbered:
+ max_digits = max(len(str(n)) for n in numbered)
+ else:
+ max_digits = 1
+
+ line_no_format = f"{{:0{max_digits}d}}"
+ line_no_width = pdfmetrics.stringWidth("0" * max_digits, body_font, body_size) + 6
+ content_width = page_width - margin_left - margin_right - line_no_width
+
+ for line_number, original in parsed_lines:
+ wrapped = iter_wrapped_lines(original, content_width, body_font, body_size)
+ for i, wline in enumerate(wrapped):
+ if y < margin_bottom + line_height:
+ c.showPage()
+ page_number += 1
+ draw_header()
+ c.setFont(body_font, body_size)
+ y = y_start
+
+ if i == 0 and line_number is not None:
+ c.drawString(margin_left, y, line_no_format.format(line_number))
+ c.drawString(margin_left + line_no_width, y, wline)
+ y -= line_height
+
+ c.save()
+ if total_lines == 0:
+ print("Warning: input file had no lines.", file=sys.stderr)
+
+
+def main() -> int:
+ require_reportlab()
+
+ parser = argparse.ArgumentParser(
+ description="Generate a PDF listing for an HP-41C program."
+ )
+ parser.add_argument("input", help="Text file with HP-41C program steps, one per line.")
+ parser.add_argument(
+ "output",
+ nargs="?",
+ help="Output PDF file (default: <input>.pdf).",
+ )
+ parser.add_argument(
+ "--name",
+ help="Program name to use in the header (default: input filename).",
+ )
+ args = parser.parse_args()
+
+ input_path = args.input
+ if not os.path.isfile(input_path):
+ print(f"Error: input file not found: {input_path}", file=sys.stderr)
+ return 2
+
+ output_path = args.output or f"{input_path}.pdf"
+ program_name = args.name or os.path.splitext(os.path.basename(input_path))[0]
+
+ with open(input_path, "r", encoding="utf-8") as f:
+ lines = [line.rstrip("\n") for line in f.readlines()]
+
+ generate_pdf(lines, output_path, program_name)
+ print(f"Wrote {output_path}")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/hp41cx-emulator/Makefile b/hp41cx-emulator/Makefile
new file mode 100644
index 0000000..03cea0d
--- /dev/null
+++ b/hp41cx-emulator/Makefile
@@ -0,0 +1,97 @@
+# Makefile for NSIM package
+#
+# $Id: Makefile 68 2005-04-23 00:34:21Z eric $
+# Copyright 1995, 2003 Eric Smith <eric@brouhaha.com>
+#
+# 17.08.2014 add Qt Gui files and splitup into
+# generic libnsim.a
+# nsim the X11 Gui application and
+# qtnsim the Qt Gui application
+#
+# NSIM is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License version 2 as published by the Free
+# Software Foundation. Note that I am not granting permission to redistribute
+# or modify NSIM under the terms of any later version of the General Public
+# License.
+#
+# This program is distributed in the hope that it will be useful (or at
+# least amusing), but WITHOUT ANY WARRANTY; without even the implied warranty
+# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program (in the file "COPYING"); if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+# -----------------------------------------------------------------------------
+# You may need to change the following definitions. In particular you will
+# need to remove the -DUSE_TIMER if you don't have the setitimer() system
+# call, and you may need to chage X11LIBS and X11INCS if X isn't in /usr/X11.
+# -----------------------------------------------------------------------------
+X11LIBS = -L/usr/X11R6/lib -lX11
+X11INCS = -I/usr/X11R6/include
+X11SRCS = xio.cpp
+X11OBJS = $(addprefix $(OBJDIR)/,$(X11SRCS:.cpp=.o))
+
+AR = ar
+CC = gcc
+CXXFLAGS = -g -O0 -Wall -std=gnu++11 -DWARNING_G $(X11INCS)
+#-DUSE_TIMER -DAUTO_POWER_OFF
+# -----------------------------------------------------------------------------
+# You shouldn't have to change anything below this point, but if you do please
+# let me know why so I can improve this Makefile.
+# -----------------------------------------------------------------------------
+
+VERSION = 0.62
+
+PROGRAMS = nsim qtnsim
+
+HEADERS = nsim.h hprom.h hplcd.h phineas.h xio.h hprom.h hpram.h ifpf.h
+LIBSRCS = nsim.cpp hprom.cpp hplcd.cpp phineas.cpp ifpf.cpp hpram.cpp
+MISC = COPYING README NEWS hp41_rom # CHANGELOG
+
+OBJDIR = obj
+
+LIBOBJS = $(addprefix $(OBJDIR)/,$(LIBSRCS:.cpp=.o))
+
+SOURCES = $(LIBSRCS) $(X11SRCS)
+OBJECTS = $(LIBOBJS) $(X11OBJS)
+DEPENDS = $(addprefix $(OBJDIR)/,$(SOURCES:.cpp=.d))
+
+LIBS = -lstdc++
+
+DISTFILES = $(MISC) Makefile $(HEADERS) $(SOURCES)
+PACKAGE = nsim
+DSTNAME = $(PACKAGE)-$(VERSION)
+
+all: libnsim.a $(PROGRAMS)
+
+$(OBJDIR):
+ -mkdir $@
+
+qtnsim: libnsim.a
+ make -C ./Qtnsim ../$@
+
+libnsim.a: $(LIBOBJS)
+ $(AR) rcs libnsim.a $(LIBOBJS)
+
+nsim: $(X11OBJS) libnsim.a Makefile
+ $(CC) -o $@ $(X11OBJS) libnsim.a $(X11LIBS) $(LIBS)
+
+
+$(OBJDIR)/%.o:%.cpp
+ $(CC) -c $(CXXFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -o $@ $<
+
+dist: $(DISTFILES)
+ -rm -rf $(DSTNAME)
+ mkdir $(DSTNAME)
+ for f in $(DISTFILES); do ln $$f $(DSTNAME)/$$f; done
+ tar --gzip -chf $(DSTNAME).tar.gz $(DSTNAME)
+ -rm -rf $(DSTNAME)
+
+clean:
+ rm -f $(PROGRAMS) $(OBJECTS) $(X11OBJS) $(DEPENDS) libnsim.a
+ make -C Qtnsim clean
+
+-include $(DEPENDS) \ No newline at end of file