uxndebug

uxn debugging suite
git clone git://bvnf.space/uxndebug.git
Log | Files | Refs | README

uxnsolve (2646B)


      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
#!/bin/sh -e

VERSION=2

[ "$#" -eq 2 ] || {
    printf "usage: %s file.tal.debug address\n" "$0" >&2
    exit 1
}
[ -r "$1" ] || {
    printf "%s: file not found\n" "$1" >&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

[ -t 1 ] && {
    red="\033[1;31m"
    yellow="\033[1;33m"
    reset="\033[m"
}

debug_file="$1"
source_file="${1%.debug}"

check_versions() {
    read -r v < "$1"
    # version 2 is compatible with version 1 for this tool
    if [ "$v" != "$VERSION" ] && [ "$v" != 1 ]; then
        printf "${yellow}ERROR${reset}: incompatible version '%s' (this tool is version %d)\n" "$v" "$VERSION" >&2
        exit 1
    fi
}

compare_file_timestamps() {
    # TODO: recurse through included files to check timestamps
    case "$(ls -t "$1" "$1.debug")" in
        "$1.debug"*) ;;
        *) printf "${yellow}WARNING${reset}: source file is newer than debug info\n" >&2 ;;
    esac
}

check_versions "$debug_file"
compare_file_timestamps "$source_file"

address="$(printf "%s" "$2" | tr 'a-f' 'A-F')"
dec_address="$(printf "16iAo%s 100-p\n" "$address" | dc || kill 0)"
# add 3 because the zeroth byte is on the third line
dec_address="$((dec_address + 3))"

[ "$dec_address" -lt 0 ] && {
    printf "address %s is in zero page, don't subtract 0x0100\n" "$address"
    exit 1
}

is_integer() {
    printf %d "$1" >/dev/null 2>&1
}

token="$(sed -n "${dec_address}p" "$debug_file" | cut -d' ' -f1)"
: "${token:?cannot 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
}

print_highlighted() {
    index="$1"
    shift
    j=1
    for tok; do
        if [ "$j" -eq "$index" ]; then
            printf " ${red}%s${reset}" "$tok"
        else
            printf " %s" "$tok"
        fi
        j="$((j + 1))"
    done
    printf "\n"
}

scan() {
    i="$1"
    lineno=0
    f="$2"
    while read -r line || return 0; do
        lineno="$((lineno + 1))"
        # word splitting is desired.
        # shellcheck disable=SC2086
        set -- $line
        i="$((i + $#))"
        for op; do case "$op" in "~"*)
                cur_line="$lineno" cur_file="$f"
                scan "$i" "${op#'~'}"
                lineno="$cur_line" f="$cur_file"
            ;; esac
        done
        [ "$i" -ge "$token" ] && {
            printf "%s line %d: " "$f" "$lineno"
            print_highlighted "$(($# - i + token))" "$@"
            exit 0
        }
    done < "$f"
}

scan 0 "$source_file"