#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" MKEXE=./mkexe TESTS=tests fail=0 total=0 assert_hex_eq() { local file=$1 offset=$2 expected=$3 label=$4 local actual actual=$(xxd -s "$offset" -l $((${#expected} / 2)) -p "$file") if [ "$actual" != "$expected" ]; then echo "FAIL: $label" echo " file: $file" echo " offset: $offset" echo " expected: $expected" echo " actual: $actual" fail=$((fail + 1)) fi } run_case() { local name=$1; shift total=$((total + 1)) if "$@" >/dev/null 2>&1; then : else echo "FAIL: $name (exit nonzero)" fail=$((fail + 1)) return fi } expect_failure() { local name=$1; shift total=$((total + 1)) if "$@" >/dev/null 2>&1; then echo "FAIL: $name (expected nonzero exit, got success)" fail=$((fail + 1)) fi } # Case 1: minimal .ihx with one byte RET at 0x4100 run_case "build-single-byte" \ $MKEXE -L 0x4100 -E 0x4100 -S 0xBFFE -o $TESTS/case1.exe $TESTS/hello.ihx assert_hex_eq $TESTS/case1.exe 0 "455845" "case1: signature EXE" assert_hex_eq $TESTS/case1.exe 3 "00" "case1: version 0" assert_hex_eq $TESTS/case1.exe 4 "00020000" "case1: offset 0x00000200" assert_hex_eq $TESTS/case1.exe 8 "0000" "case1: loader=0" assert_hex_eq $TESTS/case1.exe 0x10 "0041" "case1: load=0x4100" assert_hex_eq $TESTS/case1.exe 0x12 "0041" "case1: start=0x4100" assert_hex_eq $TESTS/case1.exe 0x14 "febf" "case1: stack=0xBFFE" assert_hex_eq $TESTS/case1.exe 0x200 "c9" "case1: image byte = RET (C9)" # File size = 512 + 1 size=$(stat -f "%z" $TESTS/case1.exe 2>/dev/null || stat -c "%s" $TESTS/case1.exe) if [ "$size" != "513" ]; then echo "FAIL: case1: file size $size != 513" fail=$((fail + 1)) fi total=$((total + 1)) # Case 2: defaults — load and start auto-derived from .ihx, stack default = 0xBFFE run_case "defaults-from-ihx" \ $MKEXE -o $TESTS/case2.exe $TESTS/hello.ihx assert_hex_eq $TESTS/case2.exe 0x10 "0041" "case2: load defaults to 0x4100 (from ihx)" assert_hex_eq $TESTS/case2.exe 0x12 "0041" "case2: start defaults to load" assert_hex_eq $TESTS/case2.exe 0x14 "febf" "case2: stack defaults to 0xBFFE" # Case 3: .bin input printf '\xC9' > $TESTS/one.bin run_case "build-from-bin" \ $MKEXE -L 0x4100 -o $TESTS/case3.exe $TESTS/one.bin assert_hex_eq $TESTS/case3.exe 0x10 "0041" "case3: bin load=0x4100" assert_hex_eq $TESTS/case3.exe 0x200 "c9" "case3: bin image" # Case 4: image extending past 0xFFFF must fail printf '\xFF%.0s' {1..4096} > $TESTS/big.bin expect_failure "reject-past-FFFF" \ $MKEXE -L 0xF800 -o $TESTS/case4.exe $TESTS/big.bin # Case 5: load above 0xFFFF must fail expect_failure "reject-load-out-of-range" \ $MKEXE -L 0x10000 -o $TESTS/case5.exe $TESTS/one.bin # Case 6: bad checksum in .ihx must fail cat > $TESTS/bad.ihx <<'EOF' :01410000C900 :00000001FF EOF expect_failure "reject-bad-checksum" \ $MKEXE -o $TESTS/case6.exe $TESTS/bad.ihx # Case 7: multi-bank IHX → loader auto-set to HOME size, bank 1 appended (16 KB) # ELA "0001" puts the next records into bank 1 (virtual 0x1C000+). # Note: Intel HEX address fields are big-endian inside the record # :01 C000 00 77 C8 means addr=0xC000 (record bytes "C000" = high then low) cat > $TESTS/banked.ihx <<'EOF' :01410000C9F5 :020000040001F9 :01C0000077C8 :00000001FF EOF run_case "banked-auto-loader" \ $MKEXE -L 0x4100 -o $TESTS/case7.exe $TESTS/banked.ihx assert_hex_eq $TESTS/case7.exe 8 "0100" "case7: loader=HOME size (1 byte)" size=$(stat -f "%z" $TESTS/case7.exe 2>/dev/null || stat -c "%s" $TESTS/case7.exe) if [ "$size" != "16897" ]; then echo "FAIL: case7: file size $size != 16897 (512 hdr + 1 HOME + 16384 bank1)" fail=$((fail + 1)) fi # Byte at file offset 0x201 (first byte of bank1) should be the 0x77 we put at bank1's 0xC000 assert_hex_eq $TESTS/case7.exe 0x201 "77" "case7: bank1 first byte" total=$((total + 9)) if [ "$fail" -gt 0 ]; then echo echo "FAILED: $fail of $total assertions/cases" exit 1 fi echo "OK: $total cases passed"