commit 81418dfc8eca7f85e745b31e2d2d15b73058a7fb
parent b97e22a90af15408ca33c0b9f30e1235881474cc
Author: aabacchus <ben@bvnf.space>
Date: Sun, 5 Jun 2022 20:19:32 +0100
naive debug file generation, add tool to parse debug file, add file format manpage
Diffstat:
A | uxndebug.5 | | | 35 | +++++++++++++++++++++++++++++++++++ |
M | uxndebug.c | | | 28 | ++++++++++++++++++++++++++++ |
A | uxnsolve | | | 50 | ++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 113 insertions(+), 0 deletions(-)
diff --git a/uxndebug.5 b/uxndebug.5
@@ -0,0 +1,35 @@
+.Dd June 5, 2022
+.Dt UXNDEBUG 5
+.Os
+.Sh NAME
+.Nm uxndebug ,
+.Nm .tal.debug
+.Nd uxntal debug info file format
+.Sh DESCRIPTION
+The format of debug information for an uxntal rom file is documented in this file.
+.Pp
+Each line of the debug file is terminated by a single
+.Aq newline
+character, and contains a single hexadecimal number.
+The line number of the file corresponds to the
+.Em byte
+of the rom,
+and the number written on the line signifies which
+.Em token
+in the source files was assembled into that
+.Em byte .
+The name of the debug file is used to identify which source file contains the relevant
+.Em token Ns s .
+.Sh FILES
+For a source file
+.Pa example.tal ,
+the corresponding rom
+.Pa example.rom
+and debug information file
+.Pa example.tal.debug
+are created by running:
+.Bd -literal -offset indent
+uxndebug example.tal example.rom
+.Ed
+.Sh SEE ALSO
+.Xr uxndebug 1
diff --git a/uxndebug.c b/uxndebug.c
@@ -48,6 +48,9 @@ static int litlast = 0;
static int jsrlast = 0;
static int token_number = 0;
+static int debug_lineno = 0;
+static char *debug_filename = "debug.tal.debug";
+FILE *debug_file = NULL;
/* clang-format off */
@@ -191,6 +194,17 @@ makereference(char *scope, char *label, Uint16 addr)
return 1;
}
+static void
+debug_next_byte(void) {
+ while (p.ptr - 0x0100 != debug_lineno) {
+ fprintf(debug_file, "\n");
+ debug_lineno++;
+ /* TODO: better way to handle this is to write to buffer with byte,token pairs, then write to file at end */
+ }
+ fprintf(debug_file, "%d\n", token_number);
+ debug_lineno++;
+}
+
static int
writebyte(Uint8 b)
{
@@ -200,6 +214,7 @@ writebyte(Uint8 b)
return error("Writing after the end of RAM", "");
else if(p.ptr < p.length)
return error("Memory overwrite", "");
+ debug_next_byte();
p.data[p.ptr++] = b;
p.length = p.ptr;
litlast = 0;
@@ -211,11 +226,14 @@ static int
writeopcode(char *w)
{
Uint8 res;
+ /* disable optimizations which go back
if(jsrlast && scmp(w, "JMP2r", 5)) { /* tail-call optimization */
+ /*
p.data[p.ptr - 1] = jsrlast == 2 ? findopcode("JMP2") : findopcode("JMP");
jsrlast = 0;
return 1;
}
+ */
res = writebyte(findopcode(w));
if(scmp(w, "JSR2", 4))
jsrlast = 2;
@@ -235,12 +253,15 @@ writeshort(Uint16 s, int lit)
static int
writelitbyte(Uint8 b)
{
+ /* disable optimizations which go back
if(litlast) { /* literals optimization */
+ /*
Uint8 hb = p.data[p.ptr - 1];
p.ptr -= 2;
p.length = p.ptr;
return writeshort((hb << 8) + b, 1);
}
+ */
if(!writebyte(findopcode("LIT"))) return 0;
if(!writebyte(b)) return 0;
litlast = 1;
@@ -454,6 +475,12 @@ int
main(int argc, char *argv[])
{
FILE *src, *dst;
+ debug_file = fopen(debug_filename, "w");
+ if (debug_file == NULL) {
+ perror(debug_filename);
+ return 1;
+ }
+
if(argc < 3)
return !error("usage", "input.tal output.rom");
if(!(src = fopen(argv[1], "r")))
@@ -466,5 +493,6 @@ main(int argc, char *argv[])
return !error("Assembly", "Output rom is empty.");
fwrite(p.data + TRIM, p.length - TRIM, 1, dst);
review(argv[2]);
+ fclose(debug_file);
return 0;
}
diff --git a/uxnsolve b/uxnsolve
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+# desired behaviour
+# shellcheck disable=SC2015
+[ "$#" -eq 2 ] && [ -r "$1" ] || {
+ printf "usage: %s file.tal.debug address\n" "$0" >&2
+ exit 1
+}
+
+case "$1" in
+ *.tal.debug) ;;
+ *)
+ printf "%s: does not end in .tal.debug, can't work out source file\n" "$1" >&2
+ exit 1
+ ;;
+esac
+
+debug_file="$1"
+source_file="${1%.debug}"
+
+address="$(printf "%s" "$2" | tr 'a-f' 'A-F')"
+dec_address="$(printf "16iAo%sp\n" "$address" | dc || kill 0)"
+# add 1 because the zeroth byte is on the first line
+dec_address="$((dec_address + 1))"
+# TODO: should we subtract the #0100 offset or request the user to do that?
+
+is_integer() {
+ printf %d "$1" >/dev/null 2>&1
+}
+
+token="$(sed -n "${dec_address}p" "$debug_file")"
+: "${token:?can\'t find integer from debug file at given byte}"
+is_integer "$token" || {
+ printf "can't find an integer token number in %s for byte %X\n" "$debug_file" "$address" >&2
+ exit 1
+}
+
+i=0
+lineno=0
+while read -r line; do
+ lineno="$((lineno + 1))"
+ # word splitting is desired.
+ # shellcheck disable=SC2086
+ set -- $line
+ i="$((i + $#))"
+ [ "$i" -ge "$token" ] && {
+ printf "line %d: %s\n" "$lineno" "$line"
+ break
+ }
+done < "$source_file"