Add full compiler toolchain, libc, examples and reference docs
First substantive commit: the entire Sprinter C compiler tree on top of
the bare README+gitignore initial commit.
What's in here:
bin/sprinter-cc — driver script invoking SDCC + linker + mkexe
libc/ — Sprinter-specific libc layer over ESTEX/BIOS
(conio, gfx, io, mem, stdio + headers)
runtime/ — crt0 variants (default/small/banked/minimal)
+ heap + bank trampolines
toolchain/ — mkexe (SprintEXE packer, C + tests)
examples/ — 30 demo programs (gfx, file I/O, env, time, …)
lib/Makefile — builds the libc archive (sprinter.lib)
docs/ — converted Sprinter manuals + asm reference samples
third_party/ — solid-c reference compiler dump + sdcc setup script
release_docs/ — packaging / release notes
gitignore overhaul:
• Drop dangerous blanket patterns: *.asm (would hide docs/samples/*.asm)
and *.exe (case-insensitive match was hiding third_party/solid-c/*.EXE
on macOS APFS). Replaced with examples/*/*.{asm,exe,…} and lib/*.lib.
• Restore tracking of toolchain/mkexe/tests/{one,big}.bin — those are
INPUT fixtures, not build outputs.
• Collapse the duplicated SDCC/C/Sdcc sections into one section per
concern (build outputs / vendored / OS-junk).
• Add .sprinter-cc-*/, build/ (catches lib/build/ too), .claude/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Executable
+125
@@ -0,0 +1,125 @@
|
||||
#!/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"
|
||||
Reference in New Issue
Block a user