diff --git a/.gitignore b/.gitignore index f6d5a10..41b833c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ tests/coremark/coreMark.bin *.swo .DS_Store *~ + +stuff/ +ROM Source Code.zip diff --git a/demos/buildGno.sh b/demos/buildGno.sh new file mode 100755 index 0000000..1f4e944 --- /dev/null +++ b/demos/buildGno.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# buildGno.sh — compile a C source into a GNO/ME-loadable OMF shell command. +# +# Usage: bash demos/buildGno.sh +# demos/.c -> demos/.omf +# +# Uses crt0Gno (GNO entry contract) + the GNO libc backend, then wraps +# the flat image in a relocatable ExpressLoad OMF via omfEmit — the +# GS/OS Loader (which GNO uses to launch commands) requires a real OMF, +# not a flat binary. + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +[ $# -ge 1 ] || { echo "usage: $0 " >&2; exit 2; } +BASE="$1" +SRC="$SCRIPT_DIR/$BASE.c" +[ -f "$SRC" ] || { echo "no source: $SRC" >&2; exit 2; } + +CLANG="$ROOT/tools/llvm-mos-build/bin/clang" +LINK="$ROOT/tools/link816" +OMF="$ROOT/tools/omfEmit" +RT="$ROOT/runtime" + +OBJ="$SCRIPT_DIR/$BASE.o" +BIN="$SCRIPT_DIR/$BASE.bin" +MAP="$SCRIPT_DIR/$BASE.map" +RELOC="$SCRIPT_DIR/$BASE.reloc" +OUT="$SCRIPT_DIR/$BASE.omf" + +echo "compile: $BASE.c -> $BASE.o" +"$CLANG" --target=w65816 -I"$RT/include" -O2 -ffunction-sections -c "$SRC" -o "$OBJ" + +echo "link: -> $BASE.bin" +"$LINK" -o "$BIN" --text-base 0x1000 --bss-base 0xA000 \ + --map "$MAP" --reloc-out "$RELOC" \ + "$RT/crt0Gno.o" "$OBJ" \ + "$RT/libcGno.o" "$RT/gnoKernel.o" "$RT/gnoGsos.o" \ + "$RT/libc.o" "$RT/snprintf.o" "$RT/extras.o" \ + "$RT/softFloat.o" "$RT/softDouble.o" \ + "$RT/libgcc.o" + +echo "OMF: -> $BASE.omf" +# Declare a dedicated DP/Stack (OMF KIND=0x1012) segment. Without it GNO +# falls back to a 4 KB default stack shared with DP and placed low in bank 0, +# which is too small / mis-placed for GS/OS file I/O: GNO's GS/OS interceptor +# (StackGSOS) carves its direct-page work area off the caller's S and the FST +# nests deep below it, corrupting the saved SP on certain stack-buffer layouts. +# A sized $12 segment makes the Loader allocate a larger bank-0 block (S set +# high in it), giving headroom and moving stack buffers off the collision. +# This is the idiomatic ORCA/GNO mechanism (== #pragma stacksize). +"$OMF" --input "$BIN" --map "$MAP" \ + --base 0x1000 --entry __start --output "$OUT" \ + --name "$(echo "$BASE" | tr '[:lower:]' '[:upper:]' | cut -c1-8)" \ + --expressload --relocs "$RELOC" --stack-size "${GNO_STACK_SIZE:-0x4000}" + +ls -la "$OUT" +echo "done: $OUT (run with: bash scripts/runInGno.sh $OUT --check 0x025000=C0DE)" diff --git a/demos/gnoCat.bin b/demos/gnoCat.bin new file mode 100644 index 0000000..b1ffe94 Binary files /dev/null and b/demos/gnoCat.bin differ diff --git a/demos/gnoCat.c b/demos/gnoCat.c new file mode 100644 index 0000000..c52ffac --- /dev/null +++ b/demos/gnoCat.c @@ -0,0 +1,15 @@ +// gnoCat.c — read one line from stdin (GNO console) and echo it. +#include +#include + +int main(int argc, char **argv) { + char line[80]; + printf("Type a line:\n"); + if (fgets(line, sizeof(line), stdin)) + printf("You typed: %s", line); + else + printf("(EOF)\n"); + *(volatile uint16_t *)0x025000UL = 0xC0DE; + for (volatile unsigned long i = 0; i < 300000UL; i++) {} + return 0; +} diff --git a/demos/gnoCat.map b/demos/gnoCat.map new file mode 100644 index 0000000..4a0af32 --- /dev/null +++ b/demos/gnoCat.map @@ -0,0 +1,221 @@ +# section layout +.text : 0x001000 .. 0x0051f7 ( 16887 bytes) +.rodata : 0x0051f7 .. 0x0056c0 ( 1225 bytes) +.bss : 0x00a000 .. 0x00a182 ( 386 bytes) + +# per-input-file .text contributions + 113 /home/scott/claude/llvm816/runtime/crt0Gno.o + 458 /home/scott/claude/llvm816/demos/gnoCat.o + 3444 /home/scott/claude/llvm816/runtime/libcGno.o + 925 /home/scott/claude/llvm816/runtime/gnoKernel.o + 34 /home/scott/claude/llvm816/runtime/gnoGsos.o + 32141 /home/scott/claude/llvm816/runtime/libc.o + 9075 /home/scott/claude/llvm816/runtime/snprintf.o + 10814 /home/scott/claude/llvm816/runtime/extras.o + 4364 /home/scott/claude/llvm816/runtime/softFloat.o + 13051 /home/scott/claude/llvm816/runtime/softDouble.o + 2552 /home/scott/claude/llvm816/runtime/libgcc.o + +# global symbols (sorted by address) +0x000000 __bss_bank +0x000000 __bss_seg0_bank +0x000000 __bss_seg1_bank +0x000000 __bss_seg1_lo16 +0x000000 __bss_seg1_size +0x000000 __bss_seg2_bank +0x000000 __bss_seg2_lo16 +0x000000 __bss_seg2_size +0x000000 __bss_seg3_bank +0x000000 __bss_seg3_lo16 +0x000000 __bss_seg3_size +0x000182 __bss_seg0_size +0x000182 __bss_size +0x001000 __start +0x001000 __text_start +0x001071 main +0x00123b gsosRead +0x001251 __putByte +0x00139e __getByte +0x00153e __gnoStartup +0x0017e0 _exit +0x001811 __gnoGsosCall +0x001826 __gnoCallNum +0x001828 __gnoPBlock +0x001833 memset +0x001891 puts +0x00194b vprintf +0x00276e writeULong +0x0028e0 writeUDec +0x002a20 writeHex +0x002baf printf +0x002c34 fgetc +0x00306c fgets +0x003170 __adddf3 +0x003cfa __subdf3 +0x003d34 __muldf3 +0x0043b7 __floatsidf +0x004535 __floatunsidf +0x004654 __fixdfsi +0x0047ff __jsl_indir +0x004802 __mulhi3 +0x004821 __umulhisi3 +0x004878 __ashlhi3 +0x004887 __lshrhi3 +0x004897 __ashrhi3 +0x0048aa __udivhi3 +0x0048b6 __umodhi3 +0x0048c2 __divhi3 +0x0048dc __modhi3 +0x0048f6 __divmod_setup +0x004929 __udivmod_core +0x004947 __mulsi3 +0x004a00 __ashlsi3 +0x004a15 __lshrsi3 +0x004a2a __ashrsi3 +0x004a44 __udivmodsi_core +0x004a7c __udivsi3 +0x004a90 __umodsi3 +0x004aa4 __divsi3 +0x004acb __modsi3 +0x004af2 __divmodsi_setup +0x004b43 __divmoddi4_stash +0x004b60 __retdi +0x004b6d __ashldi3 +0x004b90 __lshrdi3 +0x004bb3 __ashrdi3 +0x004bd9 __muldi3 +0x004c40 __ucmpdi2 +0x004c69 __cmpdi2 +0x004ca0 __udivdi3 +0x004ca9 __umoddi3 +0x004cc2 __udivmoddi_core +0x004d0f __divdi3 +0x004d2e __moddi3 +0x004d5b __absdi_a +0x004d63 __absdi_b +0x004d6b __negdi_a +0x004d89 __negdi_b +0x004da7 setjmp +0x004dcf longjmp +0x004df9 __umulhisi3_qsq +0x0051f7 __rodata_start +0x0051f7 __text_end +0x005589 writeHex.digits +0x0055a4 __mfs +0x005684 stdin +0x005688 stdout +0x00568c stderr +0x005690 __c_lconv +0x0056c0 __init_array_end +0x0056c0 __init_array_start +0x0056c0 __rodata_end +0x00a000 __bss_lo16 +0x00a000 __bss_seg0_lo16 +0x00a000 __bss_start +0x00a000 argBuf +0x00a100 argVec +0x00a180 __indirTarget +0x00a182 __bss_end +0x00a182 __heap_start +0x00bf00 __heap_end +__absdi_a = 0x004d5b +__absdi_b = 0x004d63 +__adddf3 = 0x003170 +__ashldi3 = 0x004b6d +__ashlhi3 = 0x004878 +__ashlsi3 = 0x004a00 +__ashrdi3 = 0x004bb3 +__ashrhi3 = 0x004897 +__ashrsi3 = 0x004a2a +__bss_bank = 0x000000 +__bss_end = 0x00a182 +__bss_lo16 = 0x00a000 +__bss_seg0_bank = 0x000000 +__bss_seg0_lo16 = 0x00a000 +__bss_seg0_size = 0x000182 +__bss_seg1_bank = 0x000000 +__bss_seg1_lo16 = 0x000000 +__bss_seg1_size = 0x000000 +__bss_seg2_bank = 0x000000 +__bss_seg2_lo16 = 0x000000 +__bss_seg2_size = 0x000000 +__bss_seg3_bank = 0x000000 +__bss_seg3_lo16 = 0x000000 +__bss_seg3_size = 0x000000 +__bss_size = 0x000182 +__bss_start = 0x00a000 +__c_lconv = 0x005690 +__cmpdi2 = 0x004c69 +__divdi3 = 0x004d0f +__divhi3 = 0x0048c2 +__divmod_setup = 0x0048f6 +__divmoddi4_stash = 0x004b43 +__divmodsi_setup = 0x004af2 +__divsi3 = 0x004aa4 +__fixdfsi = 0x004654 +__floatsidf = 0x0043b7 +__floatunsidf = 0x004535 +__getByte = 0x00139e +__gnoCallNum = 0x001826 +__gnoGsosCall = 0x001811 +__gnoPBlock = 0x001828 +__gnoStartup = 0x00153e +__heap_end = 0x00bf00 +__heap_start = 0x00a182 +__indirTarget = 0x00a180 +__init_array_end = 0x0056c0 +__init_array_start = 0x0056c0 +__jsl_indir = 0x0047ff +__lshrdi3 = 0x004b90 +__lshrhi3 = 0x004887 +__lshrsi3 = 0x004a15 +__mfs = 0x0055a4 +__moddi3 = 0x004d2e +__modhi3 = 0x0048dc +__modsi3 = 0x004acb +__muldf3 = 0x003d34 +__muldi3 = 0x004bd9 +__mulhi3 = 0x004802 +__mulsi3 = 0x004947 +__negdi_a = 0x004d6b +__negdi_b = 0x004d89 +__putByte = 0x001251 +__retdi = 0x004b60 +__rodata_end = 0x0056c0 +__rodata_start = 0x0051f7 +__start = 0x001000 +__subdf3 = 0x003cfa +__text_end = 0x0051f7 +__text_start = 0x001000 +__ucmpdi2 = 0x004c40 +__udivdi3 = 0x004ca0 +__udivhi3 = 0x0048aa +__udivmod_core = 0x004929 +__udivmoddi_core = 0x004cc2 +__udivmodsi_core = 0x004a44 +__udivsi3 = 0x004a7c +__umoddi3 = 0x004ca9 +__umodhi3 = 0x0048b6 +__umodsi3 = 0x004a90 +__umulhisi3 = 0x004821 +__umulhisi3_qsq = 0x004df9 +_exit = 0x0017e0 +argBuf = 0x00a000 +argVec = 0x00a100 +fgetc = 0x002c34 +fgets = 0x00306c +gsosRead = 0x00123b +longjmp = 0x004dcf +main = 0x001071 +memset = 0x001833 +printf = 0x002baf +puts = 0x001891 +setjmp = 0x004da7 +stderr = 0x00568c +stdin = 0x005684 +stdout = 0x005688 +vprintf = 0x00194b +writeHex = 0x002a20 +writeHex.digits = 0x005589 +writeUDec = 0x0028e0 +writeULong = 0x00276e diff --git a/demos/gnoCat.o b/demos/gnoCat.o new file mode 100644 index 0000000..8a86f9b Binary files /dev/null and b/demos/gnoCat.o differ diff --git a/demos/gnoCat.omf b/demos/gnoCat.omf new file mode 100644 index 0000000..09a08c3 Binary files /dev/null and b/demos/gnoCat.omf differ diff --git a/demos/gnoCat.reloc b/demos/gnoCat.reloc new file mode 100644 index 0000000..848de3e Binary files /dev/null and b/demos/gnoCat.reloc differ diff --git a/demos/gnoFile.bin b/demos/gnoFile.bin new file mode 100644 index 0000000..189e912 Binary files /dev/null and b/demos/gnoFile.bin differ diff --git a/demos/gnoFile.c b/demos/gnoFile.c new file mode 100644 index 0000000..6f08eac --- /dev/null +++ b/demos/gnoFile.c @@ -0,0 +1,31 @@ +// gnoFile.c — buffered FILE* file I/O on GNO/ME. Writes a file, reopens +// it, reads it back, and verifies the content round-trips byte-for-byte. +// Proves fopen/fwrite/fread/fclose route to real GS/OS files via libc's +// FILE* layer + GNO's inline-form GS/OS dispatch. +#include +#include +#include + +int main(int argc, char **argv) { + const char *path = "llvm816.txt"; + const char *msg = "written by llvm816 under GNO"; + + FILE *f = fopen(path, "w"); + if (!f) { printf("fopen(w) failed\n"); return 1; } + fwrite(msg, 1, strlen(msg), f); + fclose(f); + + char buf[64]; + f = fopen(path, "r"); + if (!f) { printf("fopen(r) failed\n"); return 1; } + size_t n = fread(buf, 1, sizeof(buf) - 1, f); + fclose(f); + buf[n] = 0; + + int ok = (n == strlen(msg)) && (strcmp(buf, msg) == 0); + printf("read %d bytes, content %s\n", (int)n, ok ? "OK" : "MISMATCH"); + + *(volatile uint16_t *)0x025000UL = ok ? 0xC0DE : 0xBAD0; + for (volatile unsigned long i = 0; i < 400000UL; i++) {} + return 0; +} diff --git a/demos/gnoFile.map b/demos/gnoFile.map new file mode 100644 index 0000000..5ef7640 --- /dev/null +++ b/demos/gnoFile.map @@ -0,0 +1,269 @@ +# section layout +.text : 0x001000 .. 0x006f8a ( 24458 bytes) +.rodata : 0x006f8a .. 0x007622 ( 1688 bytes) +.bss : 0x00a000 .. 0x00a402 ( 1026 bytes) + +# per-input-file .text contributions + 113 /home/scott/claude/llvm816/runtime/crt0Gno.o + 737 /home/scott/claude/llvm816/demos/gnoFile.o + 3444 /home/scott/claude/llvm816/runtime/libcGno.o + 925 /home/scott/claude/llvm816/runtime/gnoKernel.o + 34 /home/scott/claude/llvm816/runtime/gnoGsos.o + 32139 /home/scott/claude/llvm816/runtime/libc.o + 9073 /home/scott/claude/llvm816/runtime/snprintf.o + 10814 /home/scott/claude/llvm816/runtime/extras.o + 4364 /home/scott/claude/llvm816/runtime/softFloat.o + 13051 /home/scott/claude/llvm816/runtime/softDouble.o + 2552 /home/scott/claude/llvm816/runtime/libgcc.o + +# global symbols (sorted by address) +0x000000 __bss_bank +0x000000 __bss_seg0_bank +0x000000 __bss_seg1_bank +0x000000 __bss_seg1_lo16 +0x000000 __bss_seg1_size +0x000000 __bss_seg2_bank +0x000000 __bss_seg2_lo16 +0x000000 __bss_seg2_size +0x000000 __bss_seg3_bank +0x000000 __bss_seg3_lo16 +0x000000 __bss_seg3_size +0x000402 __bss_seg0_size +0x000402 __bss_size +0x001000 __start +0x001000 __text_start +0x001071 main +0x001352 gsosCreate +0x001368 gsosOpen +0x00137e gsosRead +0x001394 gsosWrite +0x0013aa gsosClose +0x0013c0 gsosGetEOF +0x0013d6 gsosSetEOF +0x0013ec gsosSetMark +0x001402 __putByte +0x00154f __putByteErr +0x00169c __gnoStartup +0x00193e _exit +0x00196f __gnoGsosCall +0x001984 __gnoCallNum +0x001986 __gnoPBlock +0x001991 memset +0x0019ef memcmp +0x001a9c puts +0x001b56 vprintf +0x002977 writeULong +0x002ae9 writeUDec +0x002c29 writeHex +0x002db8 printf +0x002e3d putcharStd +0x002e83 fclose +0x0030d3 fwrite +0x00389a fopen +0x00489c fread +0x004f03 __adddf3 +0x005a8d __subdf3 +0x005ac7 __muldf3 +0x00614a __floatsidf +0x0062c8 __floatunsidf +0x0063e7 __fixdfsi +0x006592 __jsl_indir +0x006595 __mulhi3 +0x0065b4 __umulhisi3 +0x00660b __ashlhi3 +0x00661a __lshrhi3 +0x00662a __ashrhi3 +0x00663d __udivhi3 +0x006649 __umodhi3 +0x006655 __divhi3 +0x00666f __modhi3 +0x006689 __divmod_setup +0x0066bc __udivmod_core +0x0066da __mulsi3 +0x006793 __ashlsi3 +0x0067a8 __lshrsi3 +0x0067bd __ashrsi3 +0x0067d7 __udivmodsi_core +0x00680f __udivsi3 +0x006823 __umodsi3 +0x006837 __divsi3 +0x00685e __modsi3 +0x006885 __divmodsi_setup +0x0068d6 __divmoddi4_stash +0x0068f3 __retdi +0x006900 __ashldi3 +0x006923 __lshrdi3 +0x006946 __ashrdi3 +0x00696c __muldi3 +0x0069d3 __ucmpdi2 +0x0069fc __cmpdi2 +0x006a33 __udivdi3 +0x006a3c __umoddi3 +0x006a55 __udivmoddi_core +0x006aa2 __divdi3 +0x006ac1 __moddi3 +0x006aee __absdi_a +0x006af6 __absdi_b +0x006afe __negdi_a +0x006b1c __negdi_b +0x006b3a setjmp +0x006b62 longjmp +0x006b8c __umulhisi3_qsq +0x006f8a __rodata_start +0x006f8a __text_end +0x00736f writeHex.digits +0x00738a __monthDays +0x007506 __mfs +0x0075e6 stdin +0x0075ea stdout +0x0075ee stderr +0x0075f2 __c_lconv +0x007622 __init_array_end +0x007622 __init_array_start +0x007622 __rodata_end +0x00a000 __bss_lo16 +0x00a000 __bss_seg0_lo16 +0x00a000 __bss_start +0x00a000 argBuf +0x00a100 argVec +0x00a180 freeList +0x00a184 bumpPtr +0x00a188 heapEnd +0x00a18c __atexitFn +0x00a190 errno +0x00a192 __toolboxInited +0x00a194 __vblPrev +0x00a196 __vblBase +0x00a19a __mfsReg +0x00a2ba __quickFn +0x00a2be __gsosPathBuf +0x00a3c0 __sigHandlers +0x00a400 __indirTarget +0x00a402 __bss_end +0x00a402 __heap_start +0x00bf00 __heap_end +__absdi_a = 0x006aee +__absdi_b = 0x006af6 +__adddf3 = 0x004f03 +__ashldi3 = 0x006900 +__ashlhi3 = 0x00660b +__ashlsi3 = 0x006793 +__ashrdi3 = 0x006946 +__ashrhi3 = 0x00662a +__ashrsi3 = 0x0067bd +__atexitFn = 0x00a18c +__bss_bank = 0x000000 +__bss_end = 0x00a402 +__bss_lo16 = 0x00a000 +__bss_seg0_bank = 0x000000 +__bss_seg0_lo16 = 0x00a000 +__bss_seg0_size = 0x000402 +__bss_seg1_bank = 0x000000 +__bss_seg1_lo16 = 0x000000 +__bss_seg1_size = 0x000000 +__bss_seg2_bank = 0x000000 +__bss_seg2_lo16 = 0x000000 +__bss_seg2_size = 0x000000 +__bss_seg3_bank = 0x000000 +__bss_seg3_lo16 = 0x000000 +__bss_seg3_size = 0x000000 +__bss_size = 0x000402 +__bss_start = 0x00a000 +__c_lconv = 0x0075f2 +__cmpdi2 = 0x0069fc +__divdi3 = 0x006aa2 +__divhi3 = 0x006655 +__divmod_setup = 0x006689 +__divmoddi4_stash = 0x0068d6 +__divmodsi_setup = 0x006885 +__divsi3 = 0x006837 +__fixdfsi = 0x0063e7 +__floatsidf = 0x00614a +__floatunsidf = 0x0062c8 +__gnoCallNum = 0x001984 +__gnoGsosCall = 0x00196f +__gnoPBlock = 0x001986 +__gnoStartup = 0x00169c +__gsosPathBuf = 0x00a2be +__heap_end = 0x00bf00 +__heap_start = 0x00a402 +__indirTarget = 0x00a400 +__init_array_end = 0x007622 +__init_array_start = 0x007622 +__jsl_indir = 0x006592 +__lshrdi3 = 0x006923 +__lshrhi3 = 0x00661a +__lshrsi3 = 0x0067a8 +__mfs = 0x007506 +__mfsReg = 0x00a19a +__moddi3 = 0x006ac1 +__modhi3 = 0x00666f +__modsi3 = 0x00685e +__monthDays = 0x00738a +__muldf3 = 0x005ac7 +__muldi3 = 0x00696c +__mulhi3 = 0x006595 +__mulsi3 = 0x0066da +__negdi_a = 0x006afe +__negdi_b = 0x006b1c +__putByte = 0x001402 +__putByteErr = 0x00154f +__quickFn = 0x00a2ba +__retdi = 0x0068f3 +__rodata_end = 0x007622 +__rodata_start = 0x006f8a +__sigHandlers = 0x00a3c0 +__start = 0x001000 +__subdf3 = 0x005a8d +__text_end = 0x006f8a +__text_start = 0x001000 +__toolboxInited = 0x00a192 +__ucmpdi2 = 0x0069d3 +__udivdi3 = 0x006a33 +__udivhi3 = 0x00663d +__udivmod_core = 0x0066bc +__udivmoddi_core = 0x006a55 +__udivmodsi_core = 0x0067d7 +__udivsi3 = 0x00680f +__umoddi3 = 0x006a3c +__umodhi3 = 0x006649 +__umodsi3 = 0x006823 +__umulhisi3 = 0x0065b4 +__umulhisi3_qsq = 0x006b8c +__vblBase = 0x00a196 +__vblPrev = 0x00a194 +_exit = 0x00193e +argBuf = 0x00a000 +argVec = 0x00a100 +bumpPtr = 0x00a184 +errno = 0x00a190 +fclose = 0x002e83 +fopen = 0x00389a +fread = 0x00489c +freeList = 0x00a180 +fwrite = 0x0030d3 +gsosClose = 0x0013aa +gsosCreate = 0x001352 +gsosGetEOF = 0x0013c0 +gsosOpen = 0x001368 +gsosRead = 0x00137e +gsosSetEOF = 0x0013d6 +gsosSetMark = 0x0013ec +gsosWrite = 0x001394 +heapEnd = 0x00a188 +longjmp = 0x006b62 +main = 0x001071 +memcmp = 0x0019ef +memset = 0x001991 +printf = 0x002db8 +putcharStd = 0x002e3d +puts = 0x001a9c +setjmp = 0x006b3a +stderr = 0x0075ee +stdin = 0x0075e6 +stdout = 0x0075ea +vprintf = 0x001b56 +writeHex = 0x002c29 +writeHex.digits = 0x00736f +writeUDec = 0x002ae9 +writeULong = 0x002977 diff --git a/demos/gnoFile.o b/demos/gnoFile.o new file mode 100644 index 0000000..d6204ba Binary files /dev/null and b/demos/gnoFile.o differ diff --git a/demos/gnoFile.omf b/demos/gnoFile.omf new file mode 100644 index 0000000..55dc288 Binary files /dev/null and b/demos/gnoFile.omf differ diff --git a/demos/gnoFile.reloc b/demos/gnoFile.reloc new file mode 100644 index 0000000..3484331 Binary files /dev/null and b/demos/gnoFile.reloc differ diff --git a/demos/gnoFmt.bin b/demos/gnoFmt.bin new file mode 100644 index 0000000..016fc5f Binary files /dev/null and b/demos/gnoFmt.bin differ diff --git a/demos/gnoFmt.c b/demos/gnoFmt.c new file mode 100644 index 0000000..ea30c7d --- /dev/null +++ b/demos/gnoFmt.c @@ -0,0 +1,21 @@ +// gnoFmt.c — snprintf format checks (stack %s was the bug). +// m0 = "%s" of stack str -> 0x4241 ("AB") +// m1 = "%s" of literal -> 0x5857 ("WX") +// m2 = "%ld" of 42L -> 0x3234 ("42") (original slot-alias case) +// m3 = "%d" of 1234 -> 0x3231 ("12") +#include +#include +static uint16_t two(const char *s) { return (uint16_t)((uint8_t)s[0] | ((uint8_t)s[1] << 8)); } +int main(int argc, char **argv) { + char s[8]; s[0]='A'; s[1]='B'; s[2]='C'; s[3]='D'; s[4]=0; + char o0[16]; snprintf(o0, sizeof o0, "%s", s); + char o1[16]; snprintf(o1, sizeof o1, "%s", "WXYZ"); + char o2[16]; snprintf(o2, sizeof o2, "%ld", 42L); + char o3[16]; snprintf(o3, sizeof o3, "%d", 1234); + *(volatile uint16_t *)0x025000UL = two(o0); + *(volatile uint16_t *)0x025002UL = two(o1); + *(volatile uint16_t *)0x025004UL = two(o2); + *(volatile uint16_t *)0x025006UL = two(o3); + for (volatile unsigned long i = 0; i < 300000UL; i++) {} + return 0; +} diff --git a/demos/gnoFmt.map b/demos/gnoFmt.map new file mode 100644 index 0000000..63ff30e --- /dev/null +++ b/demos/gnoFmt.map @@ -0,0 +1,203 @@ +# section layout +.text : 0x001000 .. 0x0055fe ( 17918 bytes) +.rodata : 0x0055fe .. 0x005620 ( 34 bytes) +.bss : 0x00a000 .. 0x00a18e ( 398 bytes) + +# per-input-file .text contributions + 113 /home/scott/claude/llvm816/runtime/crt0Gno.o + 628 /home/scott/claude/llvm816/demos/gnoFmt.o + 3444 /home/scott/claude/llvm816/runtime/libcGno.o + 925 /home/scott/claude/llvm816/runtime/gnoKernel.o + 34 /home/scott/claude/llvm816/runtime/gnoGsos.o + 32173 /home/scott/claude/llvm816/runtime/libc.o + 9075 /home/scott/claude/llvm816/runtime/snprintf.o + 10814 /home/scott/claude/llvm816/runtime/extras.o + 4364 /home/scott/claude/llvm816/runtime/softFloat.o + 13051 /home/scott/claude/llvm816/runtime/softDouble.o + 2552 /home/scott/claude/llvm816/runtime/libgcc.o + +# global symbols (sorted by address) +0x000000 __bss_bank +0x000000 __bss_seg0_bank +0x000000 __bss_seg1_bank +0x000000 __bss_seg1_lo16 +0x000000 __bss_seg1_size +0x000000 __bss_seg2_bank +0x000000 __bss_seg2_lo16 +0x000000 __bss_seg2_size +0x000000 __bss_seg3_bank +0x000000 __bss_seg3_lo16 +0x000000 __bss_seg3_size +0x00018e __bss_seg0_size +0x00018e __bss_size +0x001000 __start +0x001000 __text_start +0x001071 main +0x0012e5 __gnoStartup +0x001587 _exit +0x0015b8 __gnoGsosCall +0x0015cd __gnoCallNum +0x0015cf __gnoPBlock +0x0015da memset +0x001638 snprintf +0x001788 format +0x002f1d emitULong +0x0031f0 emitUDec +0x00348a emitHex +0x0036f5 __adddf3 +0x00427f __subdf3 +0x0042b9 __muldf3 +0x00493c __floatunsidf +0x004a5b __fixdfsi +0x004c06 __jsl_indir +0x004c09 __mulhi3 +0x004c28 __umulhisi3 +0x004c7f __ashlhi3 +0x004c8e __lshrhi3 +0x004c9e __ashrhi3 +0x004cb1 __udivhi3 +0x004cbd __umodhi3 +0x004cc9 __divhi3 +0x004ce3 __modhi3 +0x004cfd __divmod_setup +0x004d30 __udivmod_core +0x004d4e __mulsi3 +0x004e07 __ashlsi3 +0x004e1c __lshrsi3 +0x004e31 __ashrsi3 +0x004e4b __udivmodsi_core +0x004e83 __udivsi3 +0x004e97 __umodsi3 +0x004eab __divsi3 +0x004ed2 __modsi3 +0x004ef9 __divmodsi_setup +0x004f4a __divmoddi4_stash +0x004f67 __retdi +0x004f74 __ashldi3 +0x004f97 __lshrdi3 +0x004fba __ashrdi3 +0x004fe0 __muldi3 +0x005047 __ucmpdi2 +0x005070 __cmpdi2 +0x0050a7 __udivdi3 +0x0050b0 __umoddi3 +0x0050c9 __udivmoddi_core +0x005116 __divdi3 +0x005135 __moddi3 +0x005162 __absdi_a +0x00516a __absdi_b +0x005172 __negdi_a +0x005190 __negdi_b +0x0051ae setjmp +0x0051d6 longjmp +0x005200 __umulhisi3_qsq +0x0055fe __rodata_start +0x0055fe __text_end +0x005608 emitHex.digits +0x005620 __init_array_end +0x005620 __init_array_start +0x005620 __rodata_end +0x00a000 __bss_lo16 +0x00a000 __bss_seg0_lo16 +0x00a000 __bss_start +0x00a000 argBuf +0x00a100 argVec +0x00a180 gCur +0x00a184 gEnd +0x00a188 gTotal +0x00a18c __indirTarget +0x00a18e __bss_end +0x00a18e __heap_start +0x00bf00 __heap_end +__absdi_a = 0x005162 +__absdi_b = 0x00516a +__adddf3 = 0x0036f5 +__ashldi3 = 0x004f74 +__ashlhi3 = 0x004c7f +__ashlsi3 = 0x004e07 +__ashrdi3 = 0x004fba +__ashrhi3 = 0x004c9e +__ashrsi3 = 0x004e31 +__bss_bank = 0x000000 +__bss_end = 0x00a18e +__bss_lo16 = 0x00a000 +__bss_seg0_bank = 0x000000 +__bss_seg0_lo16 = 0x00a000 +__bss_seg0_size = 0x00018e +__bss_seg1_bank = 0x000000 +__bss_seg1_lo16 = 0x000000 +__bss_seg1_size = 0x000000 +__bss_seg2_bank = 0x000000 +__bss_seg2_lo16 = 0x000000 +__bss_seg2_size = 0x000000 +__bss_seg3_bank = 0x000000 +__bss_seg3_lo16 = 0x000000 +__bss_seg3_size = 0x000000 +__bss_size = 0x00018e +__bss_start = 0x00a000 +__cmpdi2 = 0x005070 +__divdi3 = 0x005116 +__divhi3 = 0x004cc9 +__divmod_setup = 0x004cfd +__divmoddi4_stash = 0x004f4a +__divmodsi_setup = 0x004ef9 +__divsi3 = 0x004eab +__fixdfsi = 0x004a5b +__floatunsidf = 0x00493c +__gnoCallNum = 0x0015cd +__gnoGsosCall = 0x0015b8 +__gnoPBlock = 0x0015cf +__gnoStartup = 0x0012e5 +__heap_end = 0x00bf00 +__heap_start = 0x00a18e +__indirTarget = 0x00a18c +__init_array_end = 0x005620 +__init_array_start = 0x005620 +__jsl_indir = 0x004c06 +__lshrdi3 = 0x004f97 +__lshrhi3 = 0x004c8e +__lshrsi3 = 0x004e1c +__moddi3 = 0x005135 +__modhi3 = 0x004ce3 +__modsi3 = 0x004ed2 +__muldf3 = 0x0042b9 +__muldi3 = 0x004fe0 +__mulhi3 = 0x004c09 +__mulsi3 = 0x004d4e +__negdi_a = 0x005172 +__negdi_b = 0x005190 +__retdi = 0x004f67 +__rodata_end = 0x005620 +__rodata_start = 0x0055fe +__start = 0x001000 +__subdf3 = 0x00427f +__text_end = 0x0055fe +__text_start = 0x001000 +__ucmpdi2 = 0x005047 +__udivdi3 = 0x0050a7 +__udivhi3 = 0x004cb1 +__udivmod_core = 0x004d30 +__udivmoddi_core = 0x0050c9 +__udivmodsi_core = 0x004e4b +__udivsi3 = 0x004e83 +__umoddi3 = 0x0050b0 +__umodhi3 = 0x004cbd +__umodsi3 = 0x004e97 +__umulhisi3 = 0x004c28 +__umulhisi3_qsq = 0x005200 +_exit = 0x001587 +argBuf = 0x00a000 +argVec = 0x00a100 +emitHex = 0x00348a +emitHex.digits = 0x005608 +emitUDec = 0x0031f0 +emitULong = 0x002f1d +format = 0x001788 +gCur = 0x00a180 +gEnd = 0x00a184 +gTotal = 0x00a188 +longjmp = 0x0051d6 +main = 0x001071 +memset = 0x0015da +setjmp = 0x0051ae +snprintf = 0x001638 diff --git a/demos/gnoFmt.o b/demos/gnoFmt.o new file mode 100644 index 0000000..ac755bd Binary files /dev/null and b/demos/gnoFmt.o differ diff --git a/demos/gnoFmt.omf b/demos/gnoFmt.omf new file mode 100644 index 0000000..b3dd2a6 Binary files /dev/null and b/demos/gnoFmt.omf differ diff --git a/demos/gnoFmt.reloc b/demos/gnoFmt.reloc new file mode 100644 index 0000000..c6e3679 Binary files /dev/null and b/demos/gnoFmt.reloc differ diff --git a/demos/gnoHello.bin b/demos/gnoHello.bin new file mode 100644 index 0000000..c2c0d96 Binary files /dev/null and b/demos/gnoHello.bin differ diff --git a/demos/gnoHello.c b/demos/gnoHello.c new file mode 100644 index 0000000..d73d05f --- /dev/null +++ b/demos/gnoHello.c @@ -0,0 +1,14 @@ +// gnoHello.c — GNO/ME shell command demo: prints a greeting + formatted +// output via standard stdio (libc printf/puts over the GNO console). +#include +#include + +int main(int argc, char **argv) { + puts("Hello from llvm816 on GNO/ME!"); + printf("argc=%d argv0=%s\n", argc, (argc > 0 && argv) ? argv[0] : "(none)"); + printf("sum 1..10 = %d, hex = %x\n", 55, 0xC0DE); + + *(volatile uint16_t *)0x025000UL = 0xC0DE; // harness marker + for (volatile unsigned long i = 0; i < 600000UL; i++) {} + return 0; +} diff --git a/demos/gnoHello.map b/demos/gnoHello.map new file mode 100644 index 0000000..aa80ff1 --- /dev/null +++ b/demos/gnoHello.map @@ -0,0 +1,203 @@ +# section layout +.text : 0x001000 .. 0x004b2e ( 15150 bytes) +.rodata : 0x004b2e .. 0x004f0b ( 989 bytes) +.bss : 0x00a000 .. 0x00a182 ( 386 bytes) + +# per-input-file .text contributions + 113 /home/scott/claude/llvm816/runtime/crt0Gno.o + 501 /home/scott/claude/llvm816/demos/gnoHello.o + 3444 /home/scott/claude/llvm816/runtime/libcGno.o + 925 /home/scott/claude/llvm816/runtime/gnoKernel.o + 34 /home/scott/claude/llvm816/runtime/gnoGsos.o + 32139 /home/scott/claude/llvm816/runtime/libc.o + 9073 /home/scott/claude/llvm816/runtime/snprintf.o + 10814 /home/scott/claude/llvm816/runtime/extras.o + 4364 /home/scott/claude/llvm816/runtime/softFloat.o + 13051 /home/scott/claude/llvm816/runtime/softDouble.o + 2552 /home/scott/claude/llvm816/runtime/libgcc.o + +# global symbols (sorted by address) +0x000000 __bss_bank +0x000000 __bss_seg0_bank +0x000000 __bss_seg1_bank +0x000000 __bss_seg1_lo16 +0x000000 __bss_seg1_size +0x000000 __bss_seg2_bank +0x000000 __bss_seg2_lo16 +0x000000 __bss_seg2_size +0x000000 __bss_seg3_bank +0x000000 __bss_seg3_lo16 +0x000000 __bss_seg3_size +0x000182 __bss_seg0_size +0x000182 __bss_size +0x001000 __start +0x001000 __text_start +0x001071 main +0x001266 __putByte +0x0013b3 __gnoStartup +0x001655 _exit +0x001686 __gnoGsosCall +0x00169b __gnoCallNum +0x00169d __gnoPBlock +0x0016a8 memset +0x001706 puts +0x0017c0 vprintf +0x0025e1 writeULong +0x002753 writeUDec +0x002893 writeHex +0x002a22 printf +0x002aa7 __adddf3 +0x003631 __subdf3 +0x00366b __muldf3 +0x003cee __floatsidf +0x003e6c __floatunsidf +0x003f8b __fixdfsi +0x004136 __jsl_indir +0x004139 __mulhi3 +0x004158 __umulhisi3 +0x0041af __ashlhi3 +0x0041be __lshrhi3 +0x0041ce __ashrhi3 +0x0041e1 __udivhi3 +0x0041ed __umodhi3 +0x0041f9 __divhi3 +0x004213 __modhi3 +0x00422d __divmod_setup +0x004260 __udivmod_core +0x00427e __mulsi3 +0x004337 __ashlsi3 +0x00434c __lshrsi3 +0x004361 __ashrsi3 +0x00437b __udivmodsi_core +0x0043b3 __udivsi3 +0x0043c7 __umodsi3 +0x0043db __divsi3 +0x004402 __modsi3 +0x004429 __divmodsi_setup +0x00447a __divmoddi4_stash +0x004497 __retdi +0x0044a4 __ashldi3 +0x0044c7 __lshrdi3 +0x0044ea __ashrdi3 +0x004510 __muldi3 +0x004577 __ucmpdi2 +0x0045a0 __cmpdi2 +0x0045d7 __udivdi3 +0x0045e0 __umoddi3 +0x0045f9 __udivmoddi_core +0x004646 __divdi3 +0x004665 __moddi3 +0x004692 __absdi_a +0x00469a __absdi_b +0x0046a2 __negdi_a +0x0046c0 __negdi_b +0x0046de setjmp +0x004706 longjmp +0x004730 __umulhisi3_qsq +0x004b2e __rodata_start +0x004b2e __text_end +0x004ef0 writeHex.digits +0x004f0b __init_array_end +0x004f0b __init_array_start +0x004f0b __rodata_end +0x00a000 __bss_lo16 +0x00a000 __bss_seg0_lo16 +0x00a000 __bss_start +0x00a000 argBuf +0x00a100 argVec +0x00a180 __indirTarget +0x00a182 __bss_end +0x00a182 __heap_start +0x00bf00 __heap_end +__absdi_a = 0x004692 +__absdi_b = 0x00469a +__adddf3 = 0x002aa7 +__ashldi3 = 0x0044a4 +__ashlhi3 = 0x0041af +__ashlsi3 = 0x004337 +__ashrdi3 = 0x0044ea +__ashrhi3 = 0x0041ce +__ashrsi3 = 0x004361 +__bss_bank = 0x000000 +__bss_end = 0x00a182 +__bss_lo16 = 0x00a000 +__bss_seg0_bank = 0x000000 +__bss_seg0_lo16 = 0x00a000 +__bss_seg0_size = 0x000182 +__bss_seg1_bank = 0x000000 +__bss_seg1_lo16 = 0x000000 +__bss_seg1_size = 0x000000 +__bss_seg2_bank = 0x000000 +__bss_seg2_lo16 = 0x000000 +__bss_seg2_size = 0x000000 +__bss_seg3_bank = 0x000000 +__bss_seg3_lo16 = 0x000000 +__bss_seg3_size = 0x000000 +__bss_size = 0x000182 +__bss_start = 0x00a000 +__cmpdi2 = 0x0045a0 +__divdi3 = 0x004646 +__divhi3 = 0x0041f9 +__divmod_setup = 0x00422d +__divmoddi4_stash = 0x00447a +__divmodsi_setup = 0x004429 +__divsi3 = 0x0043db +__fixdfsi = 0x003f8b +__floatsidf = 0x003cee +__floatunsidf = 0x003e6c +__gnoCallNum = 0x00169b +__gnoGsosCall = 0x001686 +__gnoPBlock = 0x00169d +__gnoStartup = 0x0013b3 +__heap_end = 0x00bf00 +__heap_start = 0x00a182 +__indirTarget = 0x00a180 +__init_array_end = 0x004f0b +__init_array_start = 0x004f0b +__jsl_indir = 0x004136 +__lshrdi3 = 0x0044c7 +__lshrhi3 = 0x0041be +__lshrsi3 = 0x00434c +__moddi3 = 0x004665 +__modhi3 = 0x004213 +__modsi3 = 0x004402 +__muldf3 = 0x00366b +__muldi3 = 0x004510 +__mulhi3 = 0x004139 +__mulsi3 = 0x00427e +__negdi_a = 0x0046a2 +__negdi_b = 0x0046c0 +__putByte = 0x001266 +__retdi = 0x004497 +__rodata_end = 0x004f0b +__rodata_start = 0x004b2e +__start = 0x001000 +__subdf3 = 0x003631 +__text_end = 0x004b2e +__text_start = 0x001000 +__ucmpdi2 = 0x004577 +__udivdi3 = 0x0045d7 +__udivhi3 = 0x0041e1 +__udivmod_core = 0x004260 +__udivmoddi_core = 0x0045f9 +__udivmodsi_core = 0x00437b +__udivsi3 = 0x0043b3 +__umoddi3 = 0x0045e0 +__umodhi3 = 0x0041ed +__umodsi3 = 0x0043c7 +__umulhisi3 = 0x004158 +__umulhisi3_qsq = 0x004730 +_exit = 0x001655 +argBuf = 0x00a000 +argVec = 0x00a100 +longjmp = 0x004706 +main = 0x001071 +memset = 0x0016a8 +printf = 0x002a22 +puts = 0x001706 +setjmp = 0x0046de +vprintf = 0x0017c0 +writeHex = 0x002893 +writeHex.digits = 0x004ef0 +writeUDec = 0x002753 +writeULong = 0x0025e1 diff --git a/demos/gnoHello.o b/demos/gnoHello.o new file mode 100644 index 0000000..9832ad1 Binary files /dev/null and b/demos/gnoHello.o differ diff --git a/demos/gnoHello.omf b/demos/gnoHello.omf new file mode 100644 index 0000000..42026f6 Binary files /dev/null and b/demos/gnoHello.omf differ diff --git a/demos/gnoHello.reloc b/demos/gnoHello.reloc new file mode 100644 index 0000000..16e1e40 Binary files /dev/null and b/demos/gnoHello.reloc differ diff --git a/demos/gnoStdin.bin b/demos/gnoStdin.bin new file mode 100644 index 0000000..3f1a1aa Binary files /dev/null and b/demos/gnoStdin.bin differ diff --git a/demos/gnoStdin.c b/demos/gnoStdin.c new file mode 100644 index 0000000..f6cc0b7 --- /dev/null +++ b/demos/gnoStdin.c @@ -0,0 +1,23 @@ +// gnoStdin.c — stdin verification, now with a printf BEFORE the read +// (mirroring gnoCat) to test whether prior stdout output corrupts fgets. +// 0xC0DE = exact "FILE-STDIN-OK"; 0xBAD1 = other; 0xBAD0 = EOF. +#include +#include +#include +int main(int argc, char **argv) { + char line[80]; + line[0] = 0; + printf("Type a line:\n"); // <-- prior stdout write, like gnoCat + char *r = fgets(line, sizeof(line), stdin); + size_t n = strlen(line); + while (n && (line[n-1] == '\n' || line[n-1] == '\r')) line[--n] = 0; + uint16_t mark; + if (r && strcmp(line, "FILE-STDIN-OK") == 0) mark = 0xC0DE; + else if (r && n > 0) mark = 0xBAD1; + else mark = 0xBAD0; + *(volatile uint16_t *)0x025000UL = mark; + *(volatile uint16_t *)0x025002UL = (uint16_t)n; + *(volatile uint16_t *)0x025004UL = (uint16_t)(uint8_t)line[0]; + for (volatile unsigned long i = 0; i < 300000UL; i++) {} + return 0; +} diff --git a/demos/gnoStdin.map b/demos/gnoStdin.map new file mode 100644 index 0000000..0f5ad8b --- /dev/null +++ b/demos/gnoStdin.map @@ -0,0 +1,201 @@ +# section layout +.text : 0x001000 .. 0x002a9c ( 6812 bytes) +.rodata : 0x002a9c .. 0x002f5f ( 1219 bytes) +.bss : 0x00a000 .. 0x00a182 ( 386 bytes) + +# per-input-file .text contributions + 113 /home/scott/claude/llvm816/runtime/crt0Gno.o + 802 /home/scott/claude/llvm816/demos/gnoStdin.o + 3444 /home/scott/claude/llvm816/runtime/libcGno.o + 925 /home/scott/claude/llvm816/runtime/gnoKernel.o + 34 /home/scott/claude/llvm816/runtime/gnoGsos.o + 32139 /home/scott/claude/llvm816/runtime/libc.o + 9073 /home/scott/claude/llvm816/runtime/snprintf.o + 10814 /home/scott/claude/llvm816/runtime/extras.o + 4364 /home/scott/claude/llvm816/runtime/softFloat.o + 13051 /home/scott/claude/llvm816/runtime/softDouble.o + 2552 /home/scott/claude/llvm816/runtime/libgcc.o + +# global symbols (sorted by address) +0x000000 __bss_bank +0x000000 __bss_seg0_bank +0x000000 __bss_seg1_bank +0x000000 __bss_seg1_lo16 +0x000000 __bss_seg1_size +0x000000 __bss_seg2_bank +0x000000 __bss_seg2_lo16 +0x000000 __bss_seg2_size +0x000000 __bss_seg3_bank +0x000000 __bss_seg3_lo16 +0x000000 __bss_seg3_size +0x000182 __bss_seg0_size +0x000182 __bss_size +0x001000 __start +0x001000 __text_start +0x001071 main +0x001393 gsosRead +0x0013a9 __putByte +0x0014f6 __getByte +0x001696 __gnoStartup +0x001938 _exit +0x001969 __gnoGsosCall +0x00197e __gnoCallNum +0x001980 __gnoPBlock +0x00198b memcmp +0x001a38 strlen +0x001aae puts +0x001b68 fgetc +0x001fa0 fgets +0x0020a4 __jsl_indir +0x0020a7 __mulhi3 +0x0020c6 __umulhisi3 +0x00211d __ashlhi3 +0x00212c __lshrhi3 +0x00213c __ashrhi3 +0x00214f __udivhi3 +0x00215b __umodhi3 +0x002167 __divhi3 +0x002181 __modhi3 +0x00219b __divmod_setup +0x0021ce __udivmod_core +0x0021ec __mulsi3 +0x0022a5 __ashlsi3 +0x0022ba __lshrsi3 +0x0022cf __ashrsi3 +0x0022e9 __udivmodsi_core +0x002321 __udivsi3 +0x002335 __umodsi3 +0x002349 __divsi3 +0x002370 __modsi3 +0x002397 __divmodsi_setup +0x0023e8 __divmoddi4_stash +0x002405 __retdi +0x002412 __ashldi3 +0x002435 __lshrdi3 +0x002458 __ashrdi3 +0x00247e __muldi3 +0x0024e5 __ucmpdi2 +0x00250e __cmpdi2 +0x002545 __udivdi3 +0x00254e __umoddi3 +0x002567 __udivmoddi_core +0x0025b4 __divdi3 +0x0025d3 __moddi3 +0x002600 __absdi_a +0x002608 __absdi_b +0x002610 __negdi_a +0x00262e __negdi_b +0x00264c setjmp +0x002674 longjmp +0x00269e __umulhisi3_qsq +0x002a9c __rodata_start +0x002a9c __text_end +0x002e28 writeHex.digits +0x002e43 __mfs +0x002f23 stdin +0x002f27 stdout +0x002f2b stderr +0x002f2f __c_lconv +0x002f5f __init_array_end +0x002f5f __init_array_start +0x002f5f __rodata_end +0x00a000 __bss_lo16 +0x00a000 __bss_seg0_lo16 +0x00a000 __bss_start +0x00a000 argBuf +0x00a100 argVec +0x00a180 __indirTarget +0x00a182 __bss_end +0x00a182 __heap_start +0x00bf00 __heap_end +__absdi_a = 0x002600 +__absdi_b = 0x002608 +__ashldi3 = 0x002412 +__ashlhi3 = 0x00211d +__ashlsi3 = 0x0022a5 +__ashrdi3 = 0x002458 +__ashrhi3 = 0x00213c +__ashrsi3 = 0x0022cf +__bss_bank = 0x000000 +__bss_end = 0x00a182 +__bss_lo16 = 0x00a000 +__bss_seg0_bank = 0x000000 +__bss_seg0_lo16 = 0x00a000 +__bss_seg0_size = 0x000182 +__bss_seg1_bank = 0x000000 +__bss_seg1_lo16 = 0x000000 +__bss_seg1_size = 0x000000 +__bss_seg2_bank = 0x000000 +__bss_seg2_lo16 = 0x000000 +__bss_seg2_size = 0x000000 +__bss_seg3_bank = 0x000000 +__bss_seg3_lo16 = 0x000000 +__bss_seg3_size = 0x000000 +__bss_size = 0x000182 +__bss_start = 0x00a000 +__c_lconv = 0x002f2f +__cmpdi2 = 0x00250e +__divdi3 = 0x0025b4 +__divhi3 = 0x002167 +__divmod_setup = 0x00219b +__divmoddi4_stash = 0x0023e8 +__divmodsi_setup = 0x002397 +__divsi3 = 0x002349 +__getByte = 0x0014f6 +__gnoCallNum = 0x00197e +__gnoGsosCall = 0x001969 +__gnoPBlock = 0x001980 +__gnoStartup = 0x001696 +__heap_end = 0x00bf00 +__heap_start = 0x00a182 +__indirTarget = 0x00a180 +__init_array_end = 0x002f5f +__init_array_start = 0x002f5f +__jsl_indir = 0x0020a4 +__lshrdi3 = 0x002435 +__lshrhi3 = 0x00212c +__lshrsi3 = 0x0022ba +__mfs = 0x002e43 +__moddi3 = 0x0025d3 +__modhi3 = 0x002181 +__modsi3 = 0x002370 +__muldi3 = 0x00247e +__mulhi3 = 0x0020a7 +__mulsi3 = 0x0021ec +__negdi_a = 0x002610 +__negdi_b = 0x00262e +__putByte = 0x0013a9 +__retdi = 0x002405 +__rodata_end = 0x002f5f +__rodata_start = 0x002a9c +__start = 0x001000 +__text_end = 0x002a9c +__text_start = 0x001000 +__ucmpdi2 = 0x0024e5 +__udivdi3 = 0x002545 +__udivhi3 = 0x00214f +__udivmod_core = 0x0021ce +__udivmoddi_core = 0x002567 +__udivmodsi_core = 0x0022e9 +__udivsi3 = 0x002321 +__umoddi3 = 0x00254e +__umodhi3 = 0x00215b +__umodsi3 = 0x002335 +__umulhisi3 = 0x0020c6 +__umulhisi3_qsq = 0x00269e +_exit = 0x001938 +argBuf = 0x00a000 +argVec = 0x00a100 +fgetc = 0x001b68 +fgets = 0x001fa0 +gsosRead = 0x001393 +longjmp = 0x002674 +main = 0x001071 +memcmp = 0x00198b +puts = 0x001aae +setjmp = 0x00264c +stderr = 0x002f2b +stdin = 0x002f23 +stdout = 0x002f27 +strlen = 0x001a38 +writeHex.digits = 0x002e28 diff --git a/demos/gnoStdin.o b/demos/gnoStdin.o new file mode 100644 index 0000000..e173e06 Binary files /dev/null and b/demos/gnoStdin.o differ diff --git a/demos/gnoStdin.omf b/demos/gnoStdin.omf new file mode 100644 index 0000000..f64ce12 Binary files /dev/null and b/demos/gnoStdin.omf differ diff --git a/demos/gnoStdin.reloc b/demos/gnoStdin.reloc new file mode 100644 index 0000000..a967143 Binary files /dev/null and b/demos/gnoStdin.reloc differ diff --git a/demos/helloBeep.bin b/demos/helloBeep.bin index ee30577..ab64853 100644 Binary files a/demos/helloBeep.bin and b/demos/helloBeep.bin differ diff --git a/demos/helloBeep.map b/demos/helloBeep.map index b60708e..d09c109 100644 --- a/demos/helloBeep.map +++ b/demos/helloBeep.map @@ -1,16 +1,16 @@ # section layout -.text : 0x001000 .. 0x001c3d ( 3133 bytes) -.rodata : 0x001c3d .. 0x001c51 ( 20 bytes) +.text : 0x001000 .. 0x001be6 ( 3046 bytes) +.rodata : 0x001be6 .. 0x001be6 ( 0 bytes) .bss : 0x00a000 .. 0x00a002 ( 2 bytes) # per-input-file .text contributions - 186 /home/scott/claude/llvm816/runtime/crt0Gsos.o + 99 /home/scott/claude/llvm816/runtime/crt0Gsos.o 395 /home/scott/claude/llvm816/demos/helloBeep.o - 30853 /home/scott/claude/llvm816/runtime/libc.o - 9098 /home/scott/claude/llvm816/runtime/snprintf.o - 10865 /home/scott/claude/llvm816/runtime/extras.o - 4374 /home/scott/claude/llvm816/runtime/softFloat.o - 13388 /home/scott/claude/llvm816/runtime/softDouble.o + 32173 /home/scott/claude/llvm816/runtime/libc.o + 9075 /home/scott/claude/llvm816/runtime/snprintf.o + 10814 /home/scott/claude/llvm816/runtime/extras.o + 4364 /home/scott/claude/llvm816/runtime/softFloat.o + 13051 /home/scott/claude/llvm816/runtime/softDouble.o 176 /home/scott/claude/llvm816/runtime/iigsGsos.o 20670 /home/scott/claude/llvm816/runtime/iigsToolbox.o 1139 /home/scott/claude/llvm816/runtime/desktop.o @@ -32,55 +32,54 @@ 0x000002 __bss_size 0x001000 __start 0x001000 __text_start -0x0010ba main -0x001245 __jsl_indir -0x001248 __mulhi3 -0x001267 __umulhisi3 -0x0012be __ashlhi3 -0x0012cd __lshrhi3 -0x0012dd __ashrhi3 -0x0012f0 __udivhi3 -0x0012fc __umodhi3 -0x001308 __divhi3 -0x001322 __modhi3 -0x00133c __divmod_setup -0x00136f __udivmod_core -0x00138d __mulsi3 -0x001446 __ashlsi3 -0x00145b __lshrsi3 -0x001470 __ashrsi3 -0x00148a __udivmodsi_core -0x0014c2 __udivsi3 -0x0014d6 __umodsi3 -0x0014ea __divsi3 -0x001511 __modsi3 -0x001538 __divmodsi_setup -0x001589 __divmoddi4_stash -0x0015a6 __retdi -0x0015b3 __ashldi3 -0x0015d6 __lshrdi3 -0x0015f9 __ashrdi3 -0x00161f __muldi3 -0x001686 __ucmpdi2 -0x0016af __cmpdi2 -0x0016e6 __udivdi3 -0x0016ef __umoddi3 -0x001708 __udivmoddi_core -0x001755 __divdi3 -0x001774 __moddi3 -0x0017a1 __absdi_a -0x0017a9 __absdi_b -0x0017b1 __negdi_a -0x0017cf __negdi_b -0x0017ed setjmp -0x001815 longjmp -0x00183f __umulhisi3_qsq -0x001c3d __rodata_start -0x001c3d __text_end -0x001c3d gChainPath -0x001c51 __init_array_end -0x001c51 __init_array_start -0x001c51 __rodata_end +0x001063 main +0x0011ee __jsl_indir +0x0011f1 __mulhi3 +0x001210 __umulhisi3 +0x001267 __ashlhi3 +0x001276 __lshrhi3 +0x001286 __ashrhi3 +0x001299 __udivhi3 +0x0012a5 __umodhi3 +0x0012b1 __divhi3 +0x0012cb __modhi3 +0x0012e5 __divmod_setup +0x001318 __udivmod_core +0x001336 __mulsi3 +0x0013ef __ashlsi3 +0x001404 __lshrsi3 +0x001419 __ashrsi3 +0x001433 __udivmodsi_core +0x00146b __udivsi3 +0x00147f __umodsi3 +0x001493 __divsi3 +0x0014ba __modsi3 +0x0014e1 __divmodsi_setup +0x001532 __divmoddi4_stash +0x00154f __retdi +0x00155c __ashldi3 +0x00157f __lshrdi3 +0x0015a2 __ashrdi3 +0x0015c8 __muldi3 +0x00162f __ucmpdi2 +0x001658 __cmpdi2 +0x00168f __udivdi3 +0x001698 __umoddi3 +0x0016b1 __udivmoddi_core +0x0016fe __divdi3 +0x00171d __moddi3 +0x00174a __absdi_a +0x001752 __absdi_b +0x00175a __negdi_a +0x001778 __negdi_b +0x001796 setjmp +0x0017be longjmp +0x0017e8 __umulhisi3_qsq +0x001be6 __init_array_end +0x001be6 __init_array_start +0x001be6 __rodata_end +0x001be6 __rodata_start +0x001be6 __text_end 0x00a000 __bss_lo16 0x00a000 __bss_seg0_lo16 0x00a000 __bss_start @@ -88,14 +87,14 @@ 0x00a002 __bss_end 0x00a002 __heap_start 0x00bf00 __heap_end -__absdi_a = 0x0017a1 -__absdi_b = 0x0017a9 -__ashldi3 = 0x0015b3 -__ashlhi3 = 0x0012be -__ashlsi3 = 0x001446 -__ashrdi3 = 0x0015f9 -__ashrhi3 = 0x0012dd -__ashrsi3 = 0x001470 +__absdi_a = 0x00174a +__absdi_b = 0x001752 +__ashldi3 = 0x00155c +__ashlhi3 = 0x001267 +__ashlsi3 = 0x0013ef +__ashrdi3 = 0x0015a2 +__ashrhi3 = 0x001286 +__ashrsi3 = 0x001419 __bss_bank = 0x000000 __bss_end = 0x00a002 __bss_lo16 = 0x00a000 @@ -113,49 +112,48 @@ __bss_seg3_lo16 = 0x000000 __bss_seg3_size = 0x000000 __bss_size = 0x000002 __bss_start = 0x00a000 -__cmpdi2 = 0x0016af -__divdi3 = 0x001755 -__divhi3 = 0x001308 -__divmod_setup = 0x00133c -__divmoddi4_stash = 0x001589 -__divmodsi_setup = 0x001538 -__divsi3 = 0x0014ea +__cmpdi2 = 0x001658 +__divdi3 = 0x0016fe +__divhi3 = 0x0012b1 +__divmod_setup = 0x0012e5 +__divmoddi4_stash = 0x001532 +__divmodsi_setup = 0x0014e1 +__divsi3 = 0x001493 __heap_end = 0x00bf00 __heap_start = 0x00a002 __indirTarget = 0x00a000 -__init_array_end = 0x001c51 -__init_array_start = 0x001c51 -__jsl_indir = 0x001245 -__lshrdi3 = 0x0015d6 -__lshrhi3 = 0x0012cd -__lshrsi3 = 0x00145b -__moddi3 = 0x001774 -__modhi3 = 0x001322 -__modsi3 = 0x001511 -__muldi3 = 0x00161f -__mulhi3 = 0x001248 -__mulsi3 = 0x00138d -__negdi_a = 0x0017b1 -__negdi_b = 0x0017cf -__retdi = 0x0015a6 -__rodata_end = 0x001c51 -__rodata_start = 0x001c3d +__init_array_end = 0x001be6 +__init_array_start = 0x001be6 +__jsl_indir = 0x0011ee +__lshrdi3 = 0x00157f +__lshrhi3 = 0x001276 +__lshrsi3 = 0x001404 +__moddi3 = 0x00171d +__modhi3 = 0x0012cb +__modsi3 = 0x0014ba +__muldi3 = 0x0015c8 +__mulhi3 = 0x0011f1 +__mulsi3 = 0x001336 +__negdi_a = 0x00175a +__negdi_b = 0x001778 +__retdi = 0x00154f +__rodata_end = 0x001be6 +__rodata_start = 0x001be6 __start = 0x001000 -__text_end = 0x001c3d +__text_end = 0x001be6 __text_start = 0x001000 -__ucmpdi2 = 0x001686 -__udivdi3 = 0x0016e6 -__udivhi3 = 0x0012f0 -__udivmod_core = 0x00136f -__udivmoddi_core = 0x001708 -__udivmodsi_core = 0x00148a -__udivsi3 = 0x0014c2 -__umoddi3 = 0x0016ef -__umodhi3 = 0x0012fc -__umodsi3 = 0x0014d6 -__umulhisi3 = 0x001267 -__umulhisi3_qsq = 0x00183f -gChainPath = 0x001c3d -longjmp = 0x001815 -main = 0x0010ba -setjmp = 0x0017ed +__ucmpdi2 = 0x00162f +__udivdi3 = 0x00168f +__udivhi3 = 0x001299 +__udivmod_core = 0x001318 +__udivmoddi_core = 0x0016b1 +__udivmodsi_core = 0x001433 +__udivsi3 = 0x00146b +__umoddi3 = 0x001698 +__umodhi3 = 0x0012a5 +__umodsi3 = 0x00147f +__umulhisi3 = 0x001210 +__umulhisi3_qsq = 0x0017e8 +longjmp = 0x0017be +main = 0x001063 +setjmp = 0x001796 diff --git a/demos/helloBeep.omf b/demos/helloBeep.omf index 74c9b23..1f9e38f 100644 Binary files a/demos/helloBeep.omf and b/demos/helloBeep.omf differ diff --git a/demos/helloBeep.reloc b/demos/helloBeep.reloc index 8f0fc6c..7eba181 100644 Binary files a/demos/helloBeep.reloc and b/demos/helloBeep.reloc differ diff --git a/demos/helloText.bin b/demos/helloText.bin index c034953..814d34b 100644 Binary files a/demos/helloText.bin and b/demos/helloText.bin differ diff --git a/demos/helloText.map b/demos/helloText.map index 01ce8b6..df5a3b0 100644 --- a/demos/helloText.map +++ b/demos/helloText.map @@ -1,16 +1,16 @@ # section layout -.text : 0x001000 .. 0x002108 ( 4360 bytes) -.rodata : 0x002108 .. 0x002176 ( 110 bytes) +.text : 0x001000 .. 0x0020b1 ( 4273 bytes) +.rodata : 0x0020b1 .. 0x00210b ( 90 bytes) .bss : 0x00a000 .. 0x00a00a ( 10 bytes) # per-input-file .text contributions - 186 /home/scott/claude/llvm816/runtime/crt0Gsos.o + 99 /home/scott/claude/llvm816/runtime/crt0Gsos.o 546 /home/scott/claude/llvm816/demos/helloText.o - 30853 /home/scott/claude/llvm816/runtime/libc.o - 9098 /home/scott/claude/llvm816/runtime/snprintf.o - 10865 /home/scott/claude/llvm816/runtime/extras.o - 4374 /home/scott/claude/llvm816/runtime/softFloat.o - 13388 /home/scott/claude/llvm816/runtime/softDouble.o + 32173 /home/scott/claude/llvm816/runtime/libc.o + 9075 /home/scott/claude/llvm816/runtime/snprintf.o + 10814 /home/scott/claude/llvm816/runtime/extras.o + 4364 /home/scott/claude/llvm816/runtime/softFloat.o + 13051 /home/scott/claude/llvm816/runtime/softDouble.o 176 /home/scott/claude/llvm816/runtime/iigsGsos.o 20670 /home/scott/claude/llvm816/runtime/iigsToolbox.o 1139 /home/scott/claude/llvm816/runtime/desktop.o @@ -32,71 +32,70 @@ 0x00000a __bss_size 0x001000 __start 0x001000 __text_start -0x0010ba main -0x0012dc CtlStartUp -0x0012ec EMStartUp -0x00130b GetNextEvent -0x001322 FMStartUp -0x001332 LEStartUp -0x001342 LoadOneTool -0x001352 NewHandle -0x001378 MenuStartUp -0x001388 QDStartUp -0x00139e DrawString -0x0013b0 MoveTo -0x0013c0 startdesk -0x0016de paintDesktopBackdrop -0x001710 __jsl_indir -0x001713 __mulhi3 -0x001732 __umulhisi3 -0x001789 __ashlhi3 -0x001798 __lshrhi3 -0x0017a8 __ashrhi3 -0x0017bb __udivhi3 -0x0017c7 __umodhi3 -0x0017d3 __divhi3 -0x0017ed __modhi3 -0x001807 __divmod_setup -0x00183a __udivmod_core -0x001858 __mulsi3 -0x001911 __ashlsi3 -0x001926 __lshrsi3 -0x00193b __ashrsi3 -0x001955 __udivmodsi_core -0x00198d __udivsi3 -0x0019a1 __umodsi3 -0x0019b5 __divsi3 -0x0019dc __modsi3 -0x001a03 __divmodsi_setup -0x001a54 __divmoddi4_stash -0x001a71 __retdi -0x001a7e __ashldi3 -0x001aa1 __lshrdi3 -0x001ac4 __ashrdi3 -0x001aea __muldi3 -0x001b51 __ucmpdi2 -0x001b7a __cmpdi2 -0x001bb1 __udivdi3 -0x001bba __umoddi3 -0x001bd3 __udivmoddi_core -0x001c20 __divdi3 -0x001c3f __moddi3 -0x001c6c __absdi_a -0x001c74 __absdi_b -0x001c7c __negdi_a -0x001c9a __negdi_b -0x001cb8 setjmp -0x001ce0 longjmp -0x001d0a __umulhisi3_qsq -0x002108 __rodata_start -0x002108 __text_end -0x002108 gChainPath -0x00211c line1 -0x002131 line2 -0x00215e line3 -0x002176 __init_array_end -0x002176 __init_array_start -0x002176 __rodata_end +0x001063 main +0x001285 CtlStartUp +0x001295 EMStartUp +0x0012b4 GetNextEvent +0x0012cb FMStartUp +0x0012db LEStartUp +0x0012eb LoadOneTool +0x0012fb NewHandle +0x001321 MenuStartUp +0x001331 QDStartUp +0x001347 DrawString +0x001359 MoveTo +0x001369 startdesk +0x001687 paintDesktopBackdrop +0x0016b9 __jsl_indir +0x0016bc __mulhi3 +0x0016db __umulhisi3 +0x001732 __ashlhi3 +0x001741 __lshrhi3 +0x001751 __ashrhi3 +0x001764 __udivhi3 +0x001770 __umodhi3 +0x00177c __divhi3 +0x001796 __modhi3 +0x0017b0 __divmod_setup +0x0017e3 __udivmod_core +0x001801 __mulsi3 +0x0018ba __ashlsi3 +0x0018cf __lshrsi3 +0x0018e4 __ashrsi3 +0x0018fe __udivmodsi_core +0x001936 __udivsi3 +0x00194a __umodsi3 +0x00195e __divsi3 +0x001985 __modsi3 +0x0019ac __divmodsi_setup +0x0019fd __divmoddi4_stash +0x001a1a __retdi +0x001a27 __ashldi3 +0x001a4a __lshrdi3 +0x001a6d __ashrdi3 +0x001a93 __muldi3 +0x001afa __ucmpdi2 +0x001b23 __cmpdi2 +0x001b5a __udivdi3 +0x001b63 __umoddi3 +0x001b7c __udivmoddi_core +0x001bc9 __divdi3 +0x001be8 __moddi3 +0x001c15 __absdi_a +0x001c1d __absdi_b +0x001c25 __negdi_a +0x001c43 __negdi_b +0x001c61 setjmp +0x001c89 longjmp +0x001cb3 __umulhisi3_qsq +0x0020b1 __rodata_start +0x0020b1 __text_end +0x0020b1 line1 +0x0020c6 line2 +0x0020f3 line3 +0x00210b __init_array_end +0x00210b __init_array_start +0x00210b __rodata_end 0x00a000 __bss_lo16 0x00a000 __bss_seg0_lo16 0x00a000 __bss_start @@ -107,25 +106,25 @@ 0x00a00a __bss_end 0x00a00a __heap_start 0x00bf00 __heap_end -CtlStartUp = 0x0012dc -DrawString = 0x00139e -EMStartUp = 0x0012ec -FMStartUp = 0x001322 -GetNextEvent = 0x00130b -LEStartUp = 0x001332 -LoadOneTool = 0x001342 -MenuStartUp = 0x001378 -MoveTo = 0x0013b0 -NewHandle = 0x001352 -QDStartUp = 0x001388 -__absdi_a = 0x001c6c -__absdi_b = 0x001c74 -__ashldi3 = 0x001a7e -__ashlhi3 = 0x001789 -__ashlsi3 = 0x001911 -__ashrdi3 = 0x001ac4 -__ashrhi3 = 0x0017a8 -__ashrsi3 = 0x00193b +CtlStartUp = 0x001285 +DrawString = 0x001347 +EMStartUp = 0x001295 +FMStartUp = 0x0012cb +GetNextEvent = 0x0012b4 +LEStartUp = 0x0012db +LoadOneTool = 0x0012eb +MenuStartUp = 0x001321 +MoveTo = 0x001359 +NewHandle = 0x0012fb +QDStartUp = 0x001331 +__absdi_a = 0x001c15 +__absdi_b = 0x001c1d +__ashldi3 = 0x001a27 +__ashlhi3 = 0x001732 +__ashlsi3 = 0x0018ba +__ashrdi3 = 0x001a6d +__ashrhi3 = 0x001751 +__ashrsi3 = 0x0018e4 __bss_bank = 0x000000 __bss_end = 0x00a00a __bss_lo16 = 0x00a000 @@ -143,57 +142,56 @@ __bss_seg3_lo16 = 0x000000 __bss_seg3_size = 0x000000 __bss_size = 0x00000a __bss_start = 0x00a000 -__cmpdi2 = 0x001b7a -__divdi3 = 0x001c20 -__divhi3 = 0x0017d3 -__divmod_setup = 0x001807 -__divmoddi4_stash = 0x001a54 -__divmodsi_setup = 0x001a03 -__divsi3 = 0x0019b5 +__cmpdi2 = 0x001b23 +__divdi3 = 0x001bc9 +__divhi3 = 0x00177c +__divmod_setup = 0x0017b0 +__divmoddi4_stash = 0x0019fd +__divmodsi_setup = 0x0019ac +__divsi3 = 0x00195e __heap_end = 0x00bf00 __heap_start = 0x00a00a __indirTarget = 0x00a008 -__init_array_end = 0x002176 -__init_array_start = 0x002176 -__jsl_indir = 0x001710 -__lshrdi3 = 0x001aa1 -__lshrhi3 = 0x001798 -__lshrsi3 = 0x001926 -__moddi3 = 0x001c3f -__modhi3 = 0x0017ed -__modsi3 = 0x0019dc -__muldi3 = 0x001aea -__mulhi3 = 0x001713 -__mulsi3 = 0x001858 -__negdi_a = 0x001c7c -__negdi_b = 0x001c9a -__retdi = 0x001a71 -__rodata_end = 0x002176 -__rodata_start = 0x002108 +__init_array_end = 0x00210b +__init_array_start = 0x00210b +__jsl_indir = 0x0016b9 +__lshrdi3 = 0x001a4a +__lshrhi3 = 0x001741 +__lshrsi3 = 0x0018cf +__moddi3 = 0x001be8 +__modhi3 = 0x001796 +__modsi3 = 0x001985 +__muldi3 = 0x001a93 +__mulhi3 = 0x0016bc +__mulsi3 = 0x001801 +__negdi_a = 0x001c25 +__negdi_b = 0x001c43 +__retdi = 0x001a1a +__rodata_end = 0x00210b +__rodata_start = 0x0020b1 __start = 0x001000 -__text_end = 0x002108 +__text_end = 0x0020b1 __text_start = 0x001000 -__ucmpdi2 = 0x001b51 -__udivdi3 = 0x001bb1 -__udivhi3 = 0x0017bb -__udivmod_core = 0x00183a -__udivmoddi_core = 0x001bd3 -__udivmodsi_core = 0x001955 -__udivsi3 = 0x00198d -__umoddi3 = 0x001bba -__umodhi3 = 0x0017c7 -__umodsi3 = 0x0019a1 -__umulhisi3 = 0x001732 -__umulhisi3_qsq = 0x001d0a -gChainPath = 0x002108 +__ucmpdi2 = 0x001afa +__udivdi3 = 0x001b5a +__udivhi3 = 0x001764 +__udivmod_core = 0x0017e3 +__udivmoddi_core = 0x001b7c +__udivmodsi_core = 0x0018fe +__udivsi3 = 0x001936 +__umoddi3 = 0x001b63 +__umodhi3 = 0x001770 +__umodsi3 = 0x00194a +__umulhisi3 = 0x0016db +__umulhisi3_qsq = 0x001cb3 gDpBase = 0x00a006 gDpHandle = 0x00a002 gUserId = 0x00a000 -line1 = 0x00211c -line2 = 0x002131 -line3 = 0x00215e -longjmp = 0x001ce0 -main = 0x0010ba -paintDesktopBackdrop = 0x0016de -setjmp = 0x001cb8 -startdesk = 0x0013c0 +line1 = 0x0020b1 +line2 = 0x0020c6 +line3 = 0x0020f3 +longjmp = 0x001c89 +main = 0x001063 +paintDesktopBackdrop = 0x001687 +setjmp = 0x001c61 +startdesk = 0x001369 diff --git a/demos/helloText.omf b/demos/helloText.omf index 0671c40..4f3c40a 100644 Binary files a/demos/helloText.omf and b/demos/helloText.omf differ diff --git a/demos/helloText.reloc b/demos/helloText.reloc index d5e0829..6177be8 100644 Binary files a/demos/helloText.reloc and b/demos/helloText.reloc differ diff --git a/runtime/build.sh b/runtime/build.sh index 5464c33..1ac56ad 100755 --- a/runtime/build.sh +++ b/runtime/build.sh @@ -39,6 +39,10 @@ cc() { asm "$SRC/crt0.s" asm "$SRC/crt0Gsos.s" +asm "$SRC/crt0Gno.s" +asm "$SRC/gnoKernel.s" +asm "$SRC/gnoGsos.s" +cc "$SRC/libcGno.c" asm "$SRC/libgcc.s" cc "$SRC/libc.c" cc "$SRC/strtol.c" diff --git a/runtime/include/gno/kernel.h b/runtime/include/gno/kernel.h new file mode 100644 index 0000000..d313c2e --- /dev/null +++ b/runtime/include/gno/kernel.h @@ -0,0 +1,48 @@ +// AUTOGENERATED by scripts/genGnoKernel.py — DO NOT EDIT. +// GNO kernel toolset $03 wrappers, callable from C. +// Convention: each K* returns the kernel result (or -1 on error); +// the last argument is `int *errno` and gets the kernel's errno. +// +// These are LOW-LEVEL primitives — libc routines in libcGno.c +// wrap them into POSIX-named fork/exec/wait/etc. +#ifndef GNO_KERNEL_H +#define GNO_KERNEL_H + +#include + +extern int Kgetpid(void); // 0x0903 +extern int Kkill(int a0, int a1, void * a2); // 0x0A03 +extern int Kfork(void * a0, void * a1); // 0x0B03 +extern int Kgetppid(void * a0); // 0x4003 +extern int Kwait(void * a0, void * a1); // 0x1703 +extern int K_execve(void * a0, void * a1, void * a2); // 0x1D03 +extern unsigned long Ksignal(int a0, void * a1, void * a2); // 0x1603 +extern unsigned long Kalarm(void * a0, void * a1); // 0x1E03 +extern unsigned long Kalarm10(void * a0, void * a1); // 0x4203 +extern int Ksigpause(void * a0, void * a1); // 0x2103 +extern unsigned long Ksigsetmask(void * a0, void * a1); // 0x1B03 +extern unsigned long Ksigblock(void * a0, void * a1); // 0x1C03 +extern int Kdup(int a0, void * a1); // 0x2203 +extern int Kdup2(int a0, int a1, void * a2); // 0x2303 +extern int Kpipe(void * a0, void * a1); // 0x2403 +extern int Kioctl(int a0, void * a1, void * a2, void * a3); // 0x2603 +extern int Kstat(void * a0, void * a1, void * a2); // 0x2703 +extern int Kfstat(int a0, void * a1, void * a2); // 0x2803 +extern int Klstat(void * a0, void * a1, void * a2); // 0x2903 +extern int Kgetuid(void * a0); // 0x2A03 +extern int Kgetgid(void * a0); // 0x2B03 +extern int Kgeteuid(void * a0); // 0x2C03 +extern int Kgetegid(void * a0); // 0x2D03 +extern int Ksetuid(int a0, void * a1); // 0x2E03 +extern int Ksetgid(int a0, void * a1); // 0x2F03 +extern int Ktcnewpgrp(int a0, void * a1); // 0x1803 +extern int Ksettpgrp(int a0, void * a1); // 0x1903 +extern int Ktctpgrp(int a0, int a1, void * a2); // 0x1A03 +extern int K_getpgrp(int a0, void * a1); // 0x2503 +extern int Ksetpgrp(int a0, int a1, void * a2); // 0x3403 +extern int Kkvm_open(void * a0); // 0x1103 +extern int Kkvm_close(void * a0, void * a1); // 0x1203 +extern unsigned long Ktimes(void * a0, void * a1); // 0x3503 +extern void KSetGNOQuitRec(int a0, void * a1, int a2, void * a3); // 0x4103 + +#endif diff --git a/runtime/src/crt0.s b/runtime/src/crt0.s index 64bad95..721f022 100644 --- a/runtime/src/crt0.s +++ b/runtime/src/crt0.s @@ -84,6 +84,9 @@ __start: phb ; save current DBR ; ---- segment 0 ---- + ; The `sep #0x20` that sets the segment's data bank also puts us in + ; M=8 for the whole store loop (X stays 16-bit), so we don't flip + ; SEP/REP per byte; each segment label below re-enters via `rep`. rep #0x20 ldx #__bss_seg0_size beq .Lbss_seg1 @@ -92,14 +95,11 @@ __start: .byte __bss_seg0_bank pha plb - rep #0x20 ldx #0 .Lbss_loop0: cpx #__bss_seg0_size bcs .Lbss_seg1 - sep #0x20 stz __bss_seg0_lo16, x - rep #0x20 inx bra .Lbss_loop0 .Lbss_seg1: @@ -112,14 +112,11 @@ __start: .byte __bss_seg1_bank pha plb - rep #0x20 ldx #0 .Lbss_loop1: cpx #__bss_seg1_size bcs .Lbss_seg2 - sep #0x20 stz __bss_seg1_lo16, x - rep #0x20 inx bra .Lbss_loop1 .Lbss_seg2: @@ -132,14 +129,11 @@ __start: .byte __bss_seg2_bank pha plb - rep #0x20 ldx #0 .Lbss_loop2: cpx #__bss_seg2_size bcs .Lbss_seg3 - sep #0x20 stz __bss_seg2_lo16, x - rep #0x20 inx bra .Lbss_loop2 .Lbss_seg3: @@ -152,17 +146,15 @@ __start: .byte __bss_seg3_bank pha plb - rep #0x20 ldx #0 .Lbss_loop3: cpx #__bss_seg3_size bcs .Lbss_done - sep #0x20 stz __bss_seg3_lo16, x - rep #0x20 inx bra .Lbss_loop3 .Lbss_done: + rep #0x20 ; back to M=16 after the M=8 store loop plb ; restore caller's DBR ; Run static constructors. The linker emits @@ -225,8 +217,12 @@ __start: ; nothing. After return, A holds the exit code. jsl main - ; Halt via BRK $00. MAME / debuggers catch this as a clean - ; program termination. - .byte 0x00, 0x00 + ; main returned. Bare metal has no OS to return to, so spin. + ; (Previously a BRK $00 here, but headless MAME mis-vectors BRK to + ; $0000 and wild-jumps, which reads as a crash. A tight loop is the + ; conventional bare-metal halt; IRQs are already masked above, so + ; this just idles.) +.Lhalt: + bra .Lhalt .size __start, . - __start diff --git a/runtime/src/crt0Gno.s b/runtime/src/crt0Gno.s new file mode 100644 index 0000000..c19963e --- /dev/null +++ b/runtime/src/crt0Gno.s @@ -0,0 +1,141 @@ +; crt0Gno.s — GNO/ME shell command crt0. +; +; Use this INSTEAD OF crt0.s / crt0Gsos.s when building an OMF intended +; to run as a GNO shell command (filetype $B5 EXE, aux 0). +; +; GNO/ME entry contract for a shell command (per ORCA's ~GNO_COMMAND): +; E=0 (native), M=0 (16-bit accumulator), X=0 (16-bit index) +; A:X = command line pointer (32-bit; A=low word, X=high word) +; Y = user ID (currently unused by us) +; DBR = bank of our entry segment (set by GS/OS Loader) +; DP = pointer to a Memory-Manager-allocated DP page +; The GS/OS Loader's launch frame on the stack is discarded by Quit. +; +; Differences from crt0Gsos.s: +; - The kernel passes a command-line pointer in A:X (not on the stack). +; - Instead of QUIT chaining to a hardcoded path, we just QUIT(pcount=0) +; so control returns to the GNO shell that launched us. +; - main() receives (int argc, char **argv) — crt0 calls a C helper +; __gnoStartup(cmdline) which parses the command line and invokes +; main(), then falls through to QUIT. + + .text + + .globl __start +__start: + ; GNO entry contract (from kern ~GNO_COMMAND, lib/libc/gno/gnocmd.asm): + ; A = user ID + ; Y:X = command-line pointer (Y = low word, X = high word) — a + ; NUL-terminated C string of the invocation command line. + ; Stash the cmdline pointer (Y:X) to bank-0 scratch $00:00B0..B3 + ; and the user ID (A) to $00:00B6, BEFORE we switch to DP=0. + ; Bank-explicit `sta long` so we don't depend on DP/DBR here. + rep #0x30 + .byte 0x8f, 0xb6, 0x00, 0x00 ; sta long $00:00B6 (user ID) + tya + .byte 0x8f, 0xb0, 0x00, 0x00 ; sta long $00:00B0 (cmdline low word = Y) + txa + .byte 0x8f, 0xb2, 0x00, 0x00 ; sta long $00:00B2 (cmdline high word = X) + + ; Set DBR := PBR (our entry-segment bank) — same Loader-contract + ; mitigation as crt0Gsos.s. + phk + plb + + ; Save GNO's per-process direct page BEFORE zeroing it. GNO's + ; GS/OS-call interceptor uses the caller's DP to identify the + ; calling process (and find its fd table). Our codegen needs + ; DP=0, so we stash GNO's DP at $00:00B4 and restore it around + ; GS/OS calls (see __gnoSaveDP / gsosWrite-under-GNO path). + rep #0x30 + tdc ; A = current (GNO) DP + .byte 0x8f, 0xb4, 0x00, 0x00 ; sta long $00:00B4 (stash GNO DP word) + + ; Set DP=0 — backend assumes DP=0 for `sta dp` / `[dp],y` etc. + lda #0 + tcd + + ; Persistent "current data bank" byte at DP $BE = PBR (our bank). + ; See crt0.s comment for rationale (codegen reads this for the + ; bank half of `&symbol` 32-bit pointers). + sep #0x20 + phk + pla + sta 0xbe + stz 0xbf ; pad so `lda 0xbe` in 16-bit M reads $00PBR + rep #0x20 + + ; BSS zero-init (DBR-relative byte stores). M is held at 8 across + ; the whole loop while X stays 16-bit, so we don't flip SEP/REP per + ; byte (llvm-mc still encodes cpx/ldx as 16-bit X-immediates in M=8). + rep #0x30 ; M=16, X=16 + sep #0x20 ; M=8 for the byte stores; X remains 16-bit + ldx #__bss_start +.Lbss_loop: + cpx #__bss_end + bcs .Lbss_done + stz 0x0000, x ; 1-byte store (M=8) + inx + bra .Lbss_loop +.Lbss_done: + rep #0x20 ; restore M=16 + + ; Walk .init_array (C++ ctors). Inlined from crt0Gsos.s. The + ; `jsl __jsl_indir` and `jsl main` operands are relocated by the + ; GS/OS Loader: omfEmit emits a cRELOC (0xF5) IMM24 site for each + ; intra-segment JSL, so the Loader patches the full 24-bit operand + ; (bank included) to the placed address. Non-zero-bank placement is + ; handled correctly as long as the OMF is built with --relocs (which + ; demos/buildGno.sh does). + rep #0x30 + ldx #__init_array_start +.Linit_loop: + cpx #__init_array_end + bcs .Linit_done + stx 0xe0 + ldy #0 + lda (0xe0), y + sta __indirTarget + phx + jsl __jsl_indir + plx + inx + inx + inx + inx + bra .Linit_loop +.Linit_done: + + ; Reload cmdline ptr from $00:00B0..$00:00B3 into A:X. + ; Use bank-explicit `lda long` so we don't depend on DBR. + rep #0x30 + .byte 0xaf, 0xb0, 0x00, 0x00 ; lda long $00:00B0 -> A (cmdline lo word) + pha ; stash A on stack briefly + .byte 0xaf, 0xb2, 0x00, 0x00 ; lda long $00:00B2 -> A (cmdline hi word) + tax ; X = cmdline hi + pla ; A = cmdline lo + + ; Call __gnoStartup(cmdline) — C helper in libcGno.c which: + ; 1. parses the command line into argc/argv, + ; 2. calls main(argc, argv), + ; 3. returns whatever main returned. + jsl __gnoStartup + + ; ---- Terminate via _exit (libcGno.c) ---- + ; __gnoStartup returned with A = main()'s exit status. _exit issues + ; GS/OS QUIT through GNO's INLINE dispatch (__gnoGsosCall), which is + ; REQUIRED under GNO: the old stack-based QUIT here (ldx #$2029 ; + ; jsl $e100a8 with the pblock pushed) was misread by GNO's OurGSOS + ; interceptor — it reads the call number + pblock from the inline + ; bytes after the jsl, not the stack — so the QUIT was mis-dispatched, + ; the process was left restartable, and GNO RE-RAN main() a second + ; time (silently draining redirected stdin on the first pass). + jsl _exit + + ; _exit does not return. If it ever does, spin rather than fall + ; into a BRK (headless MAME mis-vectors BRK to $0000, which looks + ; like a wild crash) or off the end of the segment. +.Lhang: + bra .Lhang + + .size __start, . - __start diff --git a/runtime/src/crt0Gsos.s b/runtime/src/crt0Gsos.s index 9fa865c..82382d2 100644 --- a/runtime/src/crt0Gsos.s +++ b/runtime/src/crt0Gsos.s @@ -6,9 +6,9 @@ ; - No language-card RAM enable (GS/OS configures memory). ; - No stack base reset (GS/OS allocated and set our SP). ; - Honors GS/OS's DBR=our-bank, DP=allocated-page setup. -; - On main() return, calls GS/OS QUIT(pcount=2) to chain to a -; known next application (default: /SYSTEM/START.ORIG which -; test setups must save off the original boot launcher to). +; - On main() return, calls GS/OS QUIT(pcount=0) to return control to +; the launching program (Finder / shell / boot chain) — the standard +; GS/OS application exit. ; ; Entry from the System Loader (per Apple IIgs Toolbox Reference): ; E=0 (native), M=0 (16-bit accumulator), X=0 (16-bit index) @@ -33,18 +33,6 @@ __start: ; wrong bank without this. PHK + PLB copies PBR into DBR. phk plb - ; Diagnostic: stash a marker at $00:007F via `sta long` (bank- - ; explicit, immune to DBR uncertainty) immediately on entry. - ; Runtime probes use this to detect whether the Loader actually - ; reached our code or silently rejected the OMF earlier. - ; The assembler doesn't track SEP/REP state, so the LDA #imm - ; needs explicit `.byte` form: the standard `lda #$7F` would - ; assemble as 3 bytes (16-bit imm) and the trailing $00 would - ; execute as BRK at runtime once M=8. - sep #0x20 - .byte 0xa9, 0x7f ; lda #$7F (8-bit imm) - .byte 0x8f, 0x7f, 0x00, 0x00 ; sta long $00:007F - rep #0x20 ; Set DP=0. The C compiler assumes DP=0 for all `sta dp` and ; `[dp],y`-style accesses; GS/OS hands us a Memory-Manager- @@ -69,44 +57,28 @@ __start: ; LDAi16imm_bank expansion) rep #0x20 - ; --- Diagnostic markers between crt0 phases. Bank-explicit - ; `sta long` to $00:007E so they're visible from a host probe - ; regardless of DBR/PBR. - sep #0x20 - .byte 0xa9, 0x7e ; lda #$7E - .byte 0x8f, 0x7e, 0x00, 0x00 ; sta long $00:007E (post-DP) - rep #0x20 - - ; BSS zero-init. With DBR=our bank, `stz abs,X` writes to - ; ourBank:X — correct as long as __bss_start/__bss_end fit in - ; the segment's bank. - rep #0x30 + ; BSS zero-init. With DBR=our bank, `stz abs,X` writes to + ; ourBank:X — correct as long as __bss_start/__bss_end fit in the + ; segment's bank. M held at 8 across the loop (X stays 16-bit) so + ; we don't flip SEP/REP per byte. + rep #0x30 ; M=16, X=16 + sep #0x20 ; M=8 for the byte stores; X remains 16-bit ldx #__bss_start .Lbss_loop: cpx #__bss_end bcs .Lbss_done - sep #0x20 - stz 0x0000, x - rep #0x20 + stz 0x0000, x ; 1-byte store (M=8) inx bra .Lbss_loop .Lbss_done: - sep #0x20 - .byte 0xa9, 0x7d ; lda #$7D - .byte 0x8f, 0x7d, 0x00, 0x00 ; sta long $00:007D (post-BSS) - rep #0x20 + rep #0x20 ; restore M=16 - ; Walk .init_array (C++ ctors). - ; - ; ⚠ KNOWN BROKEN under real GS/OS Loader for non-zero-bank - ; placement: `jsl __jsl_indir` bakes a bank-0 operand at link - ; time. When the Loader places us at bank $1f or similar, the - ; JSL targets bank 0 (= GS/OS code) instead of our actual bank - ; — so this loop crashes if init_array has any entries. Same - ; applies to `jsl main` below. Closing the gap requires either - ; RELOC opcode emission in omfEmit (so the Loader patches the - ; JSL bank bytes at load time) or runtime self-patching of JSL - ; opcodes in crt0. Tracked separately. + ; Walk .init_array (C++ ctors). The `jsl __jsl_indir` and the + ; `jsl main` below are relocated by the GS/OS Loader: omfEmit emits + ; a cRELOC (0xF5) IMM24 site for each intra-segment JSL, so the + ; Loader patches the full 24-bit operand (bank included) to the + ; placed address. Non-zero-bank placement works as long as the OMF + ; is built with --relocs (demos/build.sh / buildGno.sh do). rep #0x30 ldx #__init_array_start .Linit_loop: @@ -127,86 +99,31 @@ __start: bra .Linit_loop .Linit_done: - ; Marker: about to JSL main. - sep #0x20 - .byte 0xa9, 0x7c ; lda #$7C - .byte 0x8f, 0x7c, 0x00, 0x00 ; sta long $00:007C (about to call main) - rep #0x20 - ; Call main. Standard W65816 C ABI: arg0 in A; we pass none. rep #0x30 jsl main - ; ---- QUIT (pcount=2) chain to gChainPath --------------------- - ; Parm block layout in DP $80..$87: - ; $80,$81 pcount = 2 - ; $82..$85 pathname long ptr (lo, mid, bank, pad) - ; $86,$87 flags = 0 - ; - ; The path is a GSString (2-byte length + chars). It must live - ; in bank-0 memory (GS/OS reads parm fields as bank-0). DP is in - ; bank 0, so we copy the GSString from our segment into DP $A0. - + ; ---- QUIT (pcount=0): return to the launching program ---------- + ; The standard GS/OS application exit. A pcount=0 parm block (just + ; a zero pcount word) tells QUIT to return control to whoever + ; launched us (Finder, a shell, the boot chain) with default flags + ; (non-restartable) — no hardcoded chain path needed. A program + ; that wants to chain elsewhere can provide its own _exit/crt0. rep #0x30 - - ; Copy length byte first to compute total bytes to copy. - sep #0x20 - lda gChainPath ; low byte of GSString length - clc - adc #2 ; +2 for the length word itself - tay ; Y = bytes to copy (paths < 256 chars) - rep #0x20 - - ldx #0 -.LcopyPath: - sep #0x20 - lda gChainPath, x ; DBR-relative read (DBR = our bank) - sta 0xa0, x ; DP write (in bank 0) - rep #0x20 - inx - dey - bne .LcopyPath - - ; Build parm block at DP $80. - rep #0x30 - lda #2 - sta 0x80 ; pcount - - tdc - clc - adc #0xa0 - sta 0x82 ; pathname long-ptr low+mid 16 - lda #0 - sta 0x84 ; bank byte (0) + pad byte (0) - sta 0x86 ; flags = 0 - - ; Push 32-bit parm-block pointer (low half + bank-0). + stz 0x80 ; pcount = 0 (parm block at DP $0080, bank 0) tdc clc adc #0x80 - pha - pea 0 + pha ; parm-block ptr: low 16 (offset) + pea 0 ; parm-block ptr: high 16 (bank 0) ldx #0x2029 ; QUIT class-1 call number jsl 0xe100a8 ; GS/OS dispatcher - ; QUIT only returns on failure. Clean up + BRK. + ; QUIT only returns on failure. Clean up the pushed pointer and + ; spin (don't fall into a BRK, which headless MAME mis-vectors). pla pla - .byte 0x00, 0x00 +.Lhang: + bra .Lhang .size __start, . - __start - - -; gChainPath — GSString chain target for QUIT after main(). Default -; is "/SYSTEM/START.ORIG" (saved-original boot launcher). Programs -; that need a different target must rename this symbol; the linker -; resolves whichever def is present. -; -; GSString: 2-byte length word + N chars. Length here = 18 -; ("/SYSTEM/START.ORIG"). - - .section .rodata,"a" - .globl gChainPath -gChainPath: - .byte 18, 0 - .ascii "/SYSTEM/START.ORIG" diff --git a/runtime/src/gnoGsos.s b/runtime/src/gnoGsos.s new file mode 100644 index 0000000..2061eb6 --- /dev/null +++ b/runtime/src/gnoGsos.s @@ -0,0 +1,42 @@ +; gnoGsos.s — GS/OS calls from inside a GNO/ME process. +; +; GNO patches GS/OS entry $E100A8 to its interceptor (OurGSOS in +; kern/gno/gsos.asm), which reads the call number + parameter-block +; pointer from the 6 INLINE bytes after the JSL and bumps the return +; address +6 to skip them: +; +; jsl $E100A8 +; dc i2 callNum ; 2 bytes +; dc i4 pBlockPtr ; 4 bytes (bank : offset) +; <-- OurGSOS returns here +; +; The plain stack-based form (callNum in X, pBlock pushed) that bare +; GS/OS accepts does NOT work under GNO. We self-modify the inline +; callNum + pBlock operands, then dispatch. +; +; __gnoGsosCall(void *pBlock, unsigned short callNum) -> u16 error +; C ABI: arg0 (pBlock ptr32) in A:X (A=offset, X=bank); arg1 +; (callNum i16) at (4,s). Returns the GS/OS error in A (0 = OK). + + .text + .globl __gnoGsosCall +__gnoGsosCall: + rep #0x30 + sta __gnoPBlock ; inline pBlock offset (low 16) + txa + sta __gnoPBlock+2 ; inline pBlock bank+pad (X = bank : pad) + lda 4, s ; callNum from the stack + sta __gnoCallNum ; inline callNum + lda 0xb4 ; restore GNO's per-process DP + tcd + jsl 0xe100a8 +__gnoCallNum: + .word 0 ; patched: GS/OS call number +__gnoPBlock: + .long 0 ; patched: param-block pointer + ; --- OurGSOS returns here (return addr bumped +6) --- + tay ; Y = error (survives DP change) + lda #0 + tcd ; DP := 0 for the C caller + tya + rtl diff --git a/runtime/src/gnoKernel.s b/runtime/src/gnoKernel.s new file mode 100644 index 0000000..98cfec3 --- /dev/null +++ b/runtime/src/gnoKernel.s @@ -0,0 +1,852 @@ +; AUTOGENERATED by scripts/genGnoKernel.py — DO NOT EDIT by hand. +; +; GNO/ME kernel toolset $03 wrappers. +; Dispatcher: JSL $E10000 with LDX #funcId. +; +; C ABI: arg0 (i16) in A, arg0 (i32) in A:X, arg1+ on stack +; (caller-pushed, lo-word first for 32-bit values). +; Each wrapper re-pushes args in Pascal order (high-word first +; for 32-bit), allocates result space, dispatches, pops result. + + .text + +; Kgetpid(void) -> i16 +; toolset 3, func 0x0903 + .section .text.Kgetpid,"ax" + .globl Kgetpid +Kgetpid: + ; --- result space (2 bytes) --- + pea 0 + ldx #0x0903 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kkill(i16, i16, i32) -> i16 +; toolset 3, func 0x0A03 + .section .text.Kkill,"ax" + .globl Kkill +Kkill: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (2B) --- + lda 8, s + pha + ; --- arg2 (4B) --- + lda 14, s + pha + lda 14, s + pha + ldx #0x0A03 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kfork(i32, i32) -> i16 +; toolset 3, func 0x0B03 + .section .text.Kfork,"ax" + .globl Kfork +Kfork: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 12, s + pha + lda 12, s + pha + ldx #0x0B03 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kgetpid_dup(void) -> i16 +; toolset 3, func 0x0903 + .section .text.Kgetpid_dup,"ax" + .globl Kgetpid_dup +Kgetpid_dup: + ; --- result space (2 bytes) --- + pea 0 + ldx #0x0903 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kgetppid(i32) -> i16 +; toolset 3, func 0x4003 + .section .text.Kgetppid,"ax" + .globl Kgetppid +Kgetppid: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ldx #0x4003 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kwait(i32, i32) -> i16 +; toolset 3, func 0x1703 + .section .text.Kwait,"ax" + .globl Kwait +Kwait: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 12, s + pha + lda 12, s + pha + ldx #0x1703 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; K_execve(i32, i32, i32) -> i16 +; toolset 3, func 0x1D03 + .section .text.K_execve,"ax" + .globl K_execve +K_execve: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 12, s + pha + lda 12, s + pha + ; --- arg2 (4B) --- + lda 20, s + pha + lda 20, s + pha + ldx #0x1D03 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Ksignal(i16, i32, i32) -> i32 +; toolset 3, func 0x1603 + .section .text.Ksignal,"ax" + .globl Ksignal +Ksignal: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (4 bytes) --- + pea 0 + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 12, s + pha + lda 12, s + pha + ; --- arg2 (4B) --- + lda 20, s + pha + lda 20, s + pha + ldx #0x1603 + jsl 0xe10000 + pla ; result lo -> A + plx ; result hi -> X + rtl + +; Kalarm(i32, i32) -> i32 +; toolset 3, func 0x1E03 + .section .text.Kalarm,"ax" + .globl Kalarm +Kalarm: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (4 bytes) --- + pea 0 + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 14, s + pha + lda 14, s + pha + ldx #0x1E03 + jsl 0xe10000 + pla ; result lo -> A + plx ; result hi -> X + rtl + +; Kalarm10(i32, i32) -> i32 +; toolset 3, func 0x4203 + .section .text.Kalarm10,"ax" + .globl Kalarm10 +Kalarm10: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (4 bytes) --- + pea 0 + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 14, s + pha + lda 14, s + pha + ldx #0x4203 + jsl 0xe10000 + pla ; result lo -> A + plx ; result hi -> X + rtl + +; Ksigpause(i32, i32) -> i16 +; toolset 3, func 0x2103 + .section .text.Ksigpause,"ax" + .globl Ksigpause +Ksigpause: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 12, s + pha + lda 12, s + pha + ldx #0x2103 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Ksigsetmask(i32, i32) -> i32 +; toolset 3, func 0x1B03 + .section .text.Ksigsetmask,"ax" + .globl Ksigsetmask +Ksigsetmask: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (4 bytes) --- + pea 0 + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 14, s + pha + lda 14, s + pha + ldx #0x1B03 + jsl 0xe10000 + pla ; result lo -> A + plx ; result hi -> X + rtl + +; Ksigblock(i32, i32) -> i32 +; toolset 3, func 0x1C03 + .section .text.Ksigblock,"ax" + .globl Ksigblock +Ksigblock: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (4 bytes) --- + pea 0 + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 14, s + pha + lda 14, s + pha + ldx #0x1C03 + jsl 0xe10000 + pla ; result lo -> A + plx ; result hi -> X + rtl + +; Kdup(i16, i32) -> i16 +; toolset 3, func 0x2203 + .section .text.Kdup,"ax" + .globl Kdup +Kdup: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 10, s + pha + lda 10, s + pha + ldx #0x2203 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kdup2(i16, i16, i32) -> i16 +; toolset 3, func 0x2303 + .section .text.Kdup2,"ax" + .globl Kdup2 +Kdup2: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (2B) --- + lda 8, s + pha + ; --- arg2 (4B) --- + lda 14, s + pha + lda 14, s + pha + ldx #0x2303 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kpipe(i32, i32) -> i16 +; toolset 3, func 0x2403 + .section .text.Kpipe,"ax" + .globl Kpipe +Kpipe: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 12, s + pha + lda 12, s + pha + ldx #0x2403 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kioctl(i16, i32, i32, i32) -> i16 +; toolset 3, func 0x2603 + .section .text.Kioctl,"ax" + .globl Kioctl +Kioctl: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 10, s + pha + lda 10, s + pha + ; --- arg2 (4B) --- + lda 18, s + pha + lda 18, s + pha + ; --- arg3 (4B) --- + lda 26, s + pha + lda 26, s + pha + ldx #0x2603 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kstat(i32, i32, i32) -> i16 +; toolset 3, func 0x2703 + .section .text.Kstat,"ax" + .globl Kstat +Kstat: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 12, s + pha + lda 12, s + pha + ; --- arg2 (4B) --- + lda 20, s + pha + lda 20, s + pha + ldx #0x2703 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kfstat(i16, i32, i32) -> i16 +; toolset 3, func 0x2803 + .section .text.Kfstat,"ax" + .globl Kfstat +Kfstat: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 10, s + pha + lda 10, s + pha + ; --- arg2 (4B) --- + lda 18, s + pha + lda 18, s + pha + ldx #0x2803 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Klstat(i32, i32, i32) -> i16 +; toolset 3, func 0x2903 + .section .text.Klstat,"ax" + .globl Klstat +Klstat: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 12, s + pha + lda 12, s + pha + ; --- arg2 (4B) --- + lda 20, s + pha + lda 20, s + pha + ldx #0x2903 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kgetuid(i32) -> i16 +; toolset 3, func 0x2A03 + .section .text.Kgetuid,"ax" + .globl Kgetuid +Kgetuid: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ldx #0x2A03 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kgetgid(i32) -> i16 +; toolset 3, func 0x2B03 + .section .text.Kgetgid,"ax" + .globl Kgetgid +Kgetgid: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ldx #0x2B03 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kgeteuid(i32) -> i16 +; toolset 3, func 0x2C03 + .section .text.Kgeteuid,"ax" + .globl Kgeteuid +Kgeteuid: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ldx #0x2C03 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kgetegid(i32) -> i16 +; toolset 3, func 0x2D03 + .section .text.Kgetegid,"ax" + .globl Kgetegid +Kgetegid: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ldx #0x2D03 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Ksetuid(i16, i32) -> i16 +; toolset 3, func 0x2E03 + .section .text.Ksetuid,"ax" + .globl Ksetuid +Ksetuid: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 10, s + pha + lda 10, s + pha + ldx #0x2E03 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Ksetgid(i16, i32) -> i16 +; toolset 3, func 0x2F03 + .section .text.Ksetgid,"ax" + .globl Ksetgid +Ksetgid: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 10, s + pha + lda 10, s + pha + ldx #0x2F03 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Ktcnewpgrp(i16, i32) -> i16 +; toolset 3, func 0x1803 + .section .text.Ktcnewpgrp,"ax" + .globl Ktcnewpgrp +Ktcnewpgrp: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 10, s + pha + lda 10, s + pha + ldx #0x1803 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Ksettpgrp(i16, i32) -> i16 +; toolset 3, func 0x1903 + .section .text.Ksettpgrp,"ax" + .globl Ksettpgrp +Ksettpgrp: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 10, s + pha + lda 10, s + pha + ldx #0x1903 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Ktctpgrp(i16, i16, i32) -> i16 +; toolset 3, func 0x1A03 + .section .text.Ktctpgrp,"ax" + .globl Ktctpgrp +Ktctpgrp: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (2B) --- + lda 8, s + pha + ; --- arg2 (4B) --- + lda 14, s + pha + lda 14, s + pha + ldx #0x1A03 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; K_getpgrp(i16, i32) -> i16 +; toolset 3, func 0x2503 + .section .text.K_getpgrp,"ax" + .globl K_getpgrp +K_getpgrp: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 10, s + pha + lda 10, s + pha + ldx #0x2503 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Ksetpgrp(i16, i16, i32) -> i16 +; toolset 3, func 0x3403 + .section .text.Ksetpgrp,"ax" + .globl Ksetpgrp +Ksetpgrp: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (2B) --- + lda 8, s + pha + ; --- arg2 (4B) --- + lda 14, s + pha + lda 14, s + pha + ldx #0x3403 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kkvm_open(i32) -> i16 +; toolset 3, func 0x1103 + .section .text.Kkvm_open,"ax" + .globl Kkvm_open +Kkvm_open: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ldx #0x1103 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Kkvm_close(i32, i32) -> i16 +; toolset 3, func 0x1203 + .section .text.Kkvm_close,"ax" + .globl Kkvm_close +Kkvm_close: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (2 bytes) --- + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 12, s + pha + lda 12, s + pha + ldx #0x1203 + jsl 0xe10000 + pla ; result lo -> A + rtl + +; Ktimes(i32, i32) -> i32 +; toolset 3, func 0x3503 + .section .text.Ktimes,"ax" + .globl Ktimes +Ktimes: + ; --- stash arg0 (in A/X) --- + sta 0xE0 + stx 0xE2 + ; --- result space (4 bytes) --- + pea 0 + pea 0 + ; --- arg0 --- + lda 0xE2 + pha + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 14, s + pha + lda 14, s + pha + ldx #0x3503 + jsl 0xe10000 + pla ; result lo -> A + plx ; result hi -> X + rtl + +; KSetGNOQuitRec(i16, i32, i16, i32) -> void +; toolset 3, func 0x4103 + .section .text.KSetGNOQuitRec,"ax" + .globl KSetGNOQuitRec +KSetGNOQuitRec: + ; --- stash arg0 (in A) --- + sta 0xE0 + ; --- arg0 --- + lda 0xE0 + pha + ; --- arg1 (4B) --- + lda 8, s + pha + lda 8, s + pha + ; --- arg2 (2B) --- + lda 14, s + pha + ; --- arg3 (4B) --- + lda 20, s + pha + lda 20, s + pha + ldx #0x4103 + jsl 0xe10000 + rtl diff --git a/runtime/src/libc.c b/runtime/src/libc.c index de80b47..cc3ebdf 100644 --- a/runtime/src/libc.c +++ b/runtime/src/libc.c @@ -51,6 +51,14 @@ typedef struct { u16 refNum; unsigned long position; } __GsosMarkRecGS; +typedef struct { + u16 pCount; + void *pathname; + u16 access; + u16 fileType; + unsigned long auxType; + u16 storageType; +} __GsosCreateParm; // Weak so programs that never call into the GS/OS file backend don't // drag iigsGsos.o into the link. fopen guards GSOS path on a NULL // check (see __gsosAvailable below). @@ -62,6 +70,7 @@ extern u16 gsosGetEOF (__GsosEOFRecGS *p) __attribute__((weak)); extern u16 gsosSetEOF (__GsosEOFRecGS *p) __attribute__((weak)); extern u16 gsosSetMark(__GsosMarkRecGS *p) __attribute__((weak)); extern u16 gsosGetMark(__GsosMarkRecGS *p) __attribute__((weak)); +extern u16 gsosCreate (__GsosCreateParm *p) __attribute__((weak)); static int __gsosAvailable(void) { // gsosOpen is the entry point — if iigsGsos.o is linked, all the @@ -219,8 +228,16 @@ int atoi(const char *s) { // glue, or a console emulator) override this with a strong // definition. Marked `weak` so users can replace it. __attribute__((weak)) +// Console byte sink. Default writes the IIgs MAME console hook at $E2. +// A hosted environment (e.g. GNO/ME) provides a strong __putByte that +// routes to its console (see runtime/src/libcGno.c). +extern void __putByte(char c) __attribute__((weak)); + int putchar(int c) { - *(volatile char *)0xE2 = (char)c; + if (__putByte) + __putByte((char)c); + else + *(volatile char *)0xE2 = (char)c; return c; } @@ -241,7 +258,14 @@ int puts(const char *s) { // // Callers wanting non-blocking input or Event Manager integration // should call ReadCh/GetNextEvent directly via iigs/toolbox.h. +// Console byte source. Default polls the IIgs keyboard at $C000. A +// hosted environment (GNO/ME) provides a strong __getByte that reads +// its console (returns -1 on EOF). +extern int __getByte(void) __attribute__((weak)); + int getchar(void) { + if (__getByte) + return __getByte(); volatile unsigned char *kbd = (volatile unsigned char *)0xC000; volatile unsigned char *strb = (volatile unsigned char *)0xC010; while (((*kbd) & 0x80) == 0) { @@ -926,6 +950,22 @@ clock_t clock(void) { #define FILE_KIND_MEM 3 #define FILE_KIND_GSOS 4 +// Console byte sink for stderr. A hosted environment (GNO/ME) provides +// a strong __putByteErr that targets the stderr stream (GNO fd 3); when +// absent, stderr just shares stdout's sink (the historical behavior). +extern void __putByteErr(char c) __attribute__((weak)); + +// Write one byte to the stdout (kind 1) or stderr (kind 2) console +// stream. Single dispatch point so every stderr write path routes to +// the right backend hook without duplicating the test. +static int putcharStd(int kind, int c) { + if (kind == FILE_KIND_STDERR && __putByteErr) { + __putByteErr((char)c); + return c; + } + return putchar(c); +} + typedef struct __sFILE { u8 kind; u8 writable; @@ -1006,7 +1046,7 @@ int mfsUnregister(const char *path) { int fputc(int c, FILE *stream) { if (!stream) return -1; if (stream->kind == FILE_KIND_STDOUT || stream->kind == FILE_KIND_STDERR) - return putchar(c); + return putcharStd(stream->kind, c); if (stream->kind == FILE_KIND_MEM) { if (!stream->writable) { stream->err = 1; return -1; } if (stream->pos >= stream->cap) { stream->err = 1; return -1; } @@ -1030,7 +1070,7 @@ int fputc(int c, FILE *stream) { int fputs(const char *s, FILE *stream) { if (!stream || !s) return -1; if (stream->kind == FILE_KIND_STDOUT || stream->kind == FILE_KIND_STDERR) { - while (*s) { putchar(*s); s++; } + while (*s) { putcharStd(stream->kind, *s); s++; } return 0; } if (stream->kind == FILE_KIND_MEM || stream->kind == FILE_KIND_GSOS) { @@ -1083,8 +1123,21 @@ int fprintf(FILE *stream, const char *fmt, ...) { int vfprintf(FILE *stream, const char *fmt, va_list ap) { if (!stream) return -1; - if (stream->kind == FILE_KIND_STDOUT || stream->kind == FILE_KIND_STDERR) + if (stream->kind == FILE_KIND_STDOUT) return vprintf(fmt, ap); + if (stream->kind == FILE_KIND_STDERR) { + // Route formatted stderr output to the stderr console hook (GNO + // fd 3) rather than vprintf's stdout putchar. Format into a + // stack buffer, then emit byte-by-byte via putcharStd. + char tmp[256]; + int n = vsnprintf(tmp, sizeof(tmp), fmt, ap); + if (n < 0) return -1; + size_t outLen = ((size_t)n < sizeof(tmp) - 1) + ? (size_t)n : sizeof(tmp) - 1; + for (size_t i = 0; i < outLen; i++) + putcharStd(FILE_KIND_STDERR, tmp[i]); + return n; + } if (stream->kind == FILE_KIND_GSOS) { // Format into a stack buffer, then push to GS/OS via fwrite. // 256 bytes covers most format-string outputs; longer strings @@ -1269,6 +1322,16 @@ FILE *fopen(const char *path, const char *mode) { // → NULL. if (!__gsosAvailable()) return (FILE *)0; if (__buildGSString(path) < 0) return (FILE *)0; + // For write/append modes, Create the file first (GS/OS Open does + // not create). A "duplicate filename" ($47) result is fine — the + // file already exists. Guarded on the weak gsosCreate so bare + // builds without it keep the old write-existing-only behavior. + if (wantWrite && gsosCreate) { + // access $C3 (destroy/rename/write/read), fileType $04 (TXT), + // auxType 0, storageType 1 (seedling — GS/OS grows as needed). + __GsosCreateParm cp = { 5, &__gsosPathBuf, 0xC3, 0x04, 0, 1 }; + (void)gsosCreate(&cp); // ignore result; Open reports real errors + } // pCount=3 covers refNum + pathname + requestAccess. GS/OS 6.0.2 // Open ($2010) requires requestAccess to be non-zero for any actual // open; passing only pCount=2 (refNum + pathname) leaves the access @@ -1372,7 +1435,7 @@ size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { if (stream->kind == FILE_KIND_STDOUT || stream->kind == FILE_KIND_STDERR) { size_t items = 0; while (items < nmemb) { - for (size_t b = 0; b < size; b++) putchar(*in++); + for (size_t b = 0; b < size; b++) putcharStd(stream->kind, *in++); items++; } return items; diff --git a/runtime/src/libcGno.c b/runtime/src/libcGno.c new file mode 100644 index 0000000..a873365 --- /dev/null +++ b/runtime/src/libcGno.c @@ -0,0 +1,295 @@ +// libcGno.c — GNO/ME backend for our libc. +// +// Two roles: +// 1. The GNO half of stdio: provides the GS/OS class-1 call wrappers +// (gsosOpen/Read/Write/Close/...) that libc.c's FILE* layer calls +// via weak externs, using GNO's INLINE GS/OS dispatch — so fopen / +// fread / fwrite / fprintf / fgets to real GS/OS files Just Work. +// Also provides the __putByte / __getByte console hooks libc.c +// uses for putchar / getchar, routing them to the GNO console. +// 2. POSIX process glue: __gnoStartup (argv parse + main), _exit, and +// the fork/exec/wait/etc. wrappers over the K* kernel primitives. + +#include +#include + +extern int main(int argc, char **argv); + +// GS/OS class-1 paramblock shapes (layout-matched to libc.c's). +typedef struct { + uint16_t pCount; + uint16_t refNum; + void *dataBuffer; + unsigned long requestCount; + unsigned long transferCount; +} GnoIORec; +typedef struct { uint16_t pCount; uint16_t refNum; void *pathname; uint16_t requestAccess; } GnoOpenParm; +typedef struct { uint16_t pCount; uint16_t refNum; } GnoRefNumRec; +typedef struct { uint16_t pCount; uint16_t refNum; unsigned long val; } GnoEOFRec; +typedef struct { uint16_t pCount; uint16_t refNum; unsigned long val; } GnoMarkRec; +typedef struct { uint16_t pCount; void *pathname; uint16_t access; uint16_t fileType; unsigned long auxType; uint16_t storageType; } GnoCreateParm; + +// GS/OS class-1 call numbers. +#define GSOS_CREATE 0x2001 +#define GSOS_OPEN 0x2010 +#define GSOS_READ 0x2012 +#define GSOS_WRITE 0x2013 +#define GSOS_CLOSE 0x2014 +#define GSOS_SETMARK 0x2016 +#define GSOS_GETMARK 0x2017 +#define GSOS_SETEOF 0x2018 +#define GSOS_GETEOF 0x2019 + +// Generic inline-form GS/OS dispatch (asm helper, runtime/src/gnoGsos.s). +// GNO's $E100A8 interceptor reads callNum + pBlock from the inline bytes +// after the JSL; the helper self-modifies them and restores GNO's +// process DP around the call. Returns the GS/OS error (0 = OK). +extern uint16_t __gnoGsosCall(void *pBlock, unsigned short callNum); + + +// ---- GS/OS file-call wrappers (override libc.c's weak externs) ------- +// libc.c's FILE* layer (fopen with FILE_KIND_GSOS, fread/fwrite/fgetc/ +// fputc/fclose) calls these. Routing them through GNO's inline dispatch +// makes the whole buffered-stdio surface work for real GS/OS files. +uint16_t gsosCreate(GnoCreateParm *p){ return __gnoGsosCall(p, GSOS_CREATE); } +uint16_t gsosOpen(GnoOpenParm *p) { return __gnoGsosCall(p, GSOS_OPEN); } +uint16_t gsosRead(GnoIORec *p) { return __gnoGsosCall(p, GSOS_READ); } +uint16_t gsosWrite(GnoIORec *p) { return __gnoGsosCall(p, GSOS_WRITE); } +uint16_t gsosClose(GnoRefNumRec *p) { return __gnoGsosCall(p, GSOS_CLOSE); } +uint16_t gsosGetEOF(GnoEOFRec *p) { return __gnoGsosCall(p, GSOS_GETEOF); } +uint16_t gsosSetEOF(GnoEOFRec *p) { return __gnoGsosCall(p, GSOS_SETEOF); } +uint16_t gsosSetMark(GnoMarkRec *p) { return __gnoGsosCall(p, GSOS_SETMARK); } +uint16_t gsosGetMark(GnoMarkRec *p) { return __gnoGsosCall(p, GSOS_GETMARK); } + + +// ---- console hooks (override libc.c's weak __putByte/__getByte) ------ +// GNO fds are 1-based GS/OS refNums mapped through the per-process +// open-files table by the kernel's GS/OS interceptor (GNORdWr, +// kern/gno/gsos.asm:1794): stdin = 1, stdout = 2, stderr = 3 +// (include/unistd.h:51-53; main.c:388-390). A GS/OS Read/Write with +// that refNum is the exact mechanism GNO's own libc read()/write() use +// (lib/libc/sys/syscall.c:756/1271) -- the interceptor routes it to the +// file ('<'), pipe ('|'), or TTY behind the fd, so redirection is +// honored transparently. putchar/getchar in libc.c route here; the +// rest of stdio (puts/printf/fgets/...) builds on them. +// +// GNO 1-based fd indices for the inherited standard streams. +#define GNO_FD_STDIN 1 +#define GNO_FD_STDOUT 2 +#define GNO_FD_STDERR 3 + +// GS/OS error returned at end-of-file ($4C). GNO's read() treats this +// as a successful (possibly short) transfer, not a hard error +// (syscall.c:765; texttool.asm:2250). +#define GSOS_ERR_EOF 0x4C + +void __putByte(char c) { + if (c == '\n') c = '\r'; // GNO console (Apple II TTY) wants CR + GnoIORec r = { 4, GNO_FD_STDOUT, &c, 1, 0 }; + __gnoGsosCall(&r, GSOS_WRITE); +} + +// Strong override for stderr (libc.c routes FILE_KIND_STDERR here). +// stderr is fd 3 -- distinct from stdout so '2>file' redirection works. +void __putByteErr(char c) { + if (c == '\n') c = '\r'; + GnoIORec r = { 4, GNO_FD_STDERR, &c, 1, 0 }; + __gnoGsosCall(&r, GSOS_WRITE); +} + +int __getByte(void) { + unsigned char c; + GnoIORec r = { 4, GNO_FD_STDIN, &c, 1, 0 }; + uint16_t err = __gnoGsosCall(&r, GSOS_READ); + // Match GNO's read(): err 0 OR $4C is a valid transfer; only a + // zero-byte transfer is genuine EOF. Reading refNum 1 reaches the + // live inherited stdin -- the file behind '<', the pipe behind '|', + // or the console TTY -- via the kernel's GNORdWr interceptor. + if ((err != 0 && err != GSOS_ERR_EOF) || r.transferCount != 1) + return -1; // EOF / error + return (int)c; +} + + +// ---- raw POSIX read/write (used by demos that want fd-level I/O) ----- +// POSIX callers pass Unix 0/1/2; GNO's fd table is 1-based (stdin = 1), +// so translate 0/1/2 -> 1/2/3 in one place. Any other fd is assumed to +// already be a GNO 1-based fd (e.g. from pipe()/dup()) and passed +// straight through. +static uint16_t gnoFd(int fd) { + if (fd >= 0 && fd <= 2) + return (uint16_t)(fd + 1); + return (uint16_t)fd; +} + + +long write(int fd, const void *buf, unsigned long n) { + GnoIORec r = { 4, gnoFd(fd), (void *)buf, n, 0 }; + if (__gnoGsosCall(&r, GSOS_WRITE) != 0) return -1; + return (long)r.transferCount; +} + +long read(int fd, void *buf, unsigned long n) { + GnoIORec r = { 4, gnoFd(fd), buf, n, 0 }; + uint16_t err = __gnoGsosCall(&r, GSOS_READ); + if (err != 0 && err != GSOS_ERR_EOF) return -1; + return (long)r.transferCount; +} + + +// Tokenize cmdline in-place using simple whitespace rules. GNO's +// shell does proper quote handling — we'll match that once we have a +// test harness to drive it. For the initial hello-world the cmdline +// is just the program name (no args), so a degenerate parser suffices. +// +// Writes into the static argv[] and writable copy buf[]. Caps argc +// at 31; longer command lines have their tail args silently dropped. +#define ARG_MAX 32 +#define CMD_MAX 256 + +static char argBuf[CMD_MAX]; +static char *argVec[ARG_MAX]; + + +static int parseCmdline(const char *cmd, char ***argvOut) { + int n = 0; + int i = 0; + // Copy cmdline into our writable buffer up to CMD_MAX-1. + while (i < CMD_MAX - 1 && cmd[i]) { + argBuf[i] = cmd[i]; + i++; + } + argBuf[i] = 0; + + // Tokenize on whitespace. Replace separator runs with NULs, + // record argv entries pointing at the first non-NUL char of each + // run. + char *p = argBuf; + while (n < ARG_MAX - 1 && *p) { + while (*p == ' ' || *p == '\t') + p++; + if (!*p) + break; + argVec[n++] = p; + while (*p && *p != ' ' && *p != '\t') + p++; + if (*p) + *p++ = 0; + } + argVec[n] = 0; + *argvOut = argVec; + return n; +} + + +// crt0Gno.s entry point. Receives the command-line pointer the kernel +// handed us (Y:X on entry; crt0 forwards it as a ptr32 arg here). +// GNO prepends an 8-byte "BYTEWRKS" signature to the command line +// (kern/gno/sys.c builds child->args = "BYTEWRKS" + name; ~GNO_PARSEARG +// skips it with `add4 commandline,#8`). Skip those 8 bytes, parse into +// argc/argv, invoke main(), and return to crt0 for the QUIT call. +int __gnoStartup(const char *cmdline) { + char **argv; + int argc; + if (cmdline) cmdline += 8; // skip "BYTEWRKS" prefix + argc = parseCmdline(cmdline, &argv); + return main(argc, argv); +} + + +// GS/OS QUIT ($2029) paramblock. pCount=0 => plain QUIT: GNO's +// CommonQuit reaps the process and returns control to the launcher (gsh). +typedef struct { uint16_t pCount; } GnoQuitParm; + +#define GSOS_QUIT 0x2029 + +// _exit: GNO/ME program termination via GS/OS QUIT, issued through the +// SAME inline GS/OS dispatch (__gnoGsosCall) as every other call here. +// +// Critical: this MUST use the inline form, not the stack-based form +// (`ldx #$2029 ; jsl $e100a8` with the pblock pushed). GNO patches +// $E100A8 to OurGSOS, which reads the call number + pblock from the 6 +// inline bytes AFTER the jsl. The stack form leaves those inline bytes +// as whatever opcode follows, so OurGSOS mis-dispatches the QUIT and the +// process is left restartable — GNO then RE-RUNS main() (observed: +// main executes twice, silently draining redirected stdin on the first +// pass). Routing QUIT through __gnoGsosCall (inline form + process-DP +// restore) terminates cleanly, so main() runs exactly once. +__attribute__((noreturn)) +void _exit(int status) { + (void)status; + GnoQuitParm q = { 0 }; // pCount=0 — plain QUIT to launcher + __gnoGsosCall(&q, GSOS_QUIT); // inline dispatch; does not return + for (;;) { } // unreachable +} + + +// Stubs for the K*-wrapped POSIX surface. These mirror what +// runtime/src/gnoKernel.s already provides; we just rename them. +// Errno handling: each K* takes a `int *errno` arg; on failure the +// kernel writes the errno value there and returns -1. Our wrappers +// stash it into the global `errno` variable. +// +// Only the minimum needed for the demo is non-trivial — the rest +// are scaffold-only and will be filled in as we add tests. + +extern int errno; // defined in libc.c + + +int fork(void) { + int err = 0; + int r = Kfork(0, &err); + if (r < 0) errno = err; + return r; +} + + +int kill(int pid, int sig) { + int err = 0; + int r = Kkill(pid, sig, &err); + if (r < 0) errno = err; + return r; +} + + +int getpid(void) { + return Kgetpid(); +} + + +int getppid(void) { + int err = 0; + int r = Kgetppid(&err); + if (r < 0) errno = err; + return r; +} + + +int dup(int oldfd) { + int err = 0; + int r = Kdup(oldfd, &err); + if (r < 0) errno = err; + return r; +} + + +int dup2(int oldfd, int newfd) { + int err = 0; + int r = Kdup2(oldfd, newfd, &err); + if (r < 0) errno = err; + return r; +} + + +int pipe(int fds[2]) { + int err = 0; + int r = Kpipe(fds, &err); + if (r < 0) errno = err; + return r; +} + + +unsigned long alarm(unsigned long seconds) { + int err = 0; + return Kalarm((void *)seconds, &err); +} diff --git a/runtime/src/snprintf.c b/runtime/src/snprintf.c index 158c0ae..025a40f 100644 --- a/runtime/src/snprintf.c +++ b/runtime/src/snprintf.c @@ -204,9 +204,6 @@ static void emitDouble(double v, int prec, char spec) { // fmt is arg0 (A register); see banner comment for why the order matters. -// Previously optnone (slot-alias bug under p:16:16; see -// feedback_snprintf_va_arg_slot_alias.md). Re-enabled greedy under -// ptr32 — testing whether the bug recurs. static int format(const char *fmt, va_list ap) { while (*fmt) { char c = *fmt++; diff --git a/scripts/genGnoKernel.py b/scripts/genGnoKernel.py new file mode 100644 index 0000000..11ace72 --- /dev/null +++ b/scripts/genGnoKernel.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +# genGnoKernel.py — generate wrappers for the GNO kernel toolset. +# +# The GNO kernel is implemented as Apple IIgs user toolset $03. Each +# kernel function is identified by a 16-bit number $XX03 where XX is +# the function index and 03 is the toolset. The dispatcher is the +# standard IIgs tool dispatcher at $E10000. +# +# Function list is hard-coded from include/gno/kerntool.h in the GNO +# Consortium source (https://github.com/GnoConsortium/gno). The +# canonical ORCA-style signature is preserved in a comment. +# +# Output: runtime/src/gnoKernel.s — asm wrappers callable from our C +# ABI (arg0 in A, arg0 i32 in A:X, rest pushed RTL on stack). Each +# wrapper re-pushes args in toolbox/Pascal order (high-word first for +# 32-bit values), allocates result space, JSLs $E10000, pops the +# result back into A:X if non-void. +# +# Run after editing the function table: +# python3 scripts/genGnoKernel.py +# +# The libc layer in runtime/src/libcGno.c wraps these K* primitives +# into POSIX names (fork, exec, wait, etc.). + +from pathlib import Path + +OUT_ASM = Path("/home/scott/claude/llvm816/runtime/src/gnoKernel.s") +OUT_HEADER = Path("/home/scott/claude/llvm816/runtime/include/gno/kernel.h") + +# Each entry: (funcId, name, retSize, [argSize, ...]) +# Sizes in bytes (2 = i16, 4 = i32 / ptr32). Names follow ORCA's K* prefix. +# +# Pulled from include/gno/kerntool.h in the GNO Consortium repo. +SYSCALLS = [ + # ---- Process control ---- + (0x0903, "Kgetpid", 2, []), # () -> int + (0x0A03, "Kkill", 2, [2, 2, 4]), # (pid, sig, *errno) -> int + (0x0B03, "Kfork", 2, [4, 4]), # (*subr, *errno) -> int + (0x0903, "Kgetpid_dup", 2, []), # alias kept for sanity check + (0x4003, "Kgetppid", 2, [4]), # (*errno) -> int + (0x1703, "Kwait", 2, [4, 4]), # (*status, *errno) -> int + (0x1D03, "K_execve", 2, [4, 4, 4]), # (*file, *cmdline, *err) -> int + (0x1603, "Ksignal", 4, [2, 4, 4]), # (sig, func_ptr, *err) -> sig_t + (0x1E03, "Kalarm", 4, [4, 4]), # (seconds, *errno) -> longword + (0x4203, "Kalarm10", 4, [4, 4]), # (seconds-tenths, *errno) -> longword + (0x2103, "Ksigpause", 2, [4, 4]), # (mask, *errno) -> int + (0x1B03, "Ksigsetmask", 4, [4, 4]), # (mask, *errno) -> longword + (0x1C03, "Ksigblock", 4, [4, 4]), # (mask, *errno) -> longword + + # ---- File descriptors ---- + (0x2203, "Kdup", 2, [2, 4]), # (oldfd, *errno) -> int + (0x2303, "Kdup2", 2, [2, 2, 4]), # (oldfd, newfd, *errno) -> int + (0x2403, "Kpipe", 2, [4, 4]), # (*fildes[2], *errno) -> int + (0x2603, "Kioctl", 2, [2, 4, 4, 4]), # (fd, req:ulong, *ptr, *errno) -> int + + # ---- stat ---- + (0x2703, "Kstat", 2, [4, 4, 4]), # (*path, *sbuf, *errno) -> int + (0x2803, "Kfstat", 2, [2, 4, 4]), # (fd, *sbuf, *errno) -> int + (0x2903, "Klstat", 2, [4, 4, 4]), # (*path, *sbuf, *errno) -> int + + # ---- IDs ---- + (0x2A03, "Kgetuid", 2, [4]), # (*errno) -> int + (0x2B03, "Kgetgid", 2, [4]), # (*errno) -> int + (0x2C03, "Kgeteuid", 2, [4]), # (*errno) -> int + (0x2D03, "Kgetegid", 2, [4]), # (*errno) -> int + (0x2E03, "Ksetuid", 2, [2, 4]), # (uid, *errno) -> int + (0x2F03, "Ksetgid", 2, [2, 4]), # (gid, *errno) -> int + + # ---- Process groups / TTY ---- + (0x1803, "Ktcnewpgrp", 2, [2, 4]), # (fdtty, *errno) -> int + (0x1903, "Ksettpgrp", 2, [2, 4]), # (fdtty, *errno) -> int + (0x1A03, "Ktctpgrp", 2, [2, 2, 4]), # (fdtty, pid, *err) -> int + (0x2503, "K_getpgrp", 2, [2, 4]), # (pid, *errno) -> pid_t + (0x3403, "Ksetpgrp", 2, [2, 2, 4]), # (pid, pgrp, *errno) -> int + + # ---- Kernel VM (process inspection) ---- + (0x1103, "Kkvm_open", 2, [4]), # (*errno) -> int + (0x1203, "Kkvm_close", 2, [4, 4]), # (kvmt, *errno) -> int + + # ---- Misc ---- + (0x3503, "Ktimes", 4, [4, 4]), # (*tms, *errno) -> clock_t + + # ---- Quit setup ---- + (0x4103, "KSetGNOQuitRec", 0, [2, 4, 2, 4]), # (pCount, GSStringPtr, flags, *err) -> void +] + +DISPATCHER = 0xE10000 + + +def emitWrapper(funcId, name, retSize, argSizes): + """Emit one .s function body for the given GNO kernel call.""" + lines = [] + argTypes = [f"i{size*8}" for size in argSizes] + lines.append(f"; {name}({', '.join(argTypes) or 'void'}) -> " + f"{'void' if retSize == 0 else f'i{retSize*8}'}") + lines.append(f"; toolset 3, func 0x{funcId:04X}") + lines.append(f"\t.section .text.{name},\"ax\"") + lines.append(f"\t.globl {name}") + lines.append(f"{name}:") + + scratchDP = 0xE0 + firstArgIs32 = bool(argSizes) and argSizes[0] == 4 + + # Stash arg0 if any args. arg0 in A (i16) or A:X (i32). + if argSizes: + lines.append(f"\t; --- stash arg0 (in A{'/X' if firstArgIs32 else ''}) ---") + lines.append(f"\tsta 0x{scratchDP:02X}") + if firstArgIs32: + lines.append(f"\tstx 0x{scratchDP + 2:02X}") + + # Result space (rounded up to whole words). + resultWords = (retSize + 1) // 2 + if resultWords > 0: + lines.append(f"\t; --- result space ({retSize} bytes) ---") + for _ in range(resultWords): + lines.append("\tpea 0") + + # Push args in Pascal order (high-word first for 32-bit). + pushedBytes = resultWords * 2 + + # arg0. + if argSizes: + lines.append("\t; --- arg0 ---") + if firstArgIs32: + lines.append(f"\tlda 0x{scratchDP + 2:02X}") + lines.append("\tpha") + pushedBytes += 2 + lines.append(f"\tlda 0x{scratchDP:02X}") + lines.append("\tpha") + pushedBytes += 2 + + # arg1+. + stackArgOffset = 4 # caller's first stack arg (after 3-byte JSL ret addr) + for i, size in enumerate(argSizes[1:], start=1): + lines.append(f"\t; --- arg{i} ({size}B) ---") + if size <= 2: + lines.append(f"\tlda {stackArgOffset + pushedBytes}, s") + lines.append("\tpha") + pushedBytes += 2 + stackArgOffset += 2 + elif size == 4: + # High word first. Caller pushed low-word-first, so HI is at + # caller offset +2. After PHA, the LO becomes accessible at + # the SAME stack-rel offset (PHA shifted SP by 2). + lines.append(f"\tlda {stackArgOffset + pushedBytes + 2}, s") + lines.append("\tpha") + pushedBytes += 2 + lines.append(f"\tlda {stackArgOffset + pushedBytes}, s") + lines.append("\tpha") + pushedBytes += 2 + stackArgOffset += 4 + else: + raise RuntimeError(f"unhandled arg size {size} in {name}") + + # Dispatch. + lines.append(f"\tldx #0x{funcId:04X}") + lines.append(f"\tjsl 0x{DISPATCHER:x}") + + # Pop result. + if resultWords > 0: + lines.append("\tpla ; result lo -> A") + if resultWords == 2: + lines.append("\tplx ; result hi -> X") + lines.append("\trtl") + lines.append("") + return lines + + +def emitHeader(): + """Generate a C header with extern decls for use by libc.""" + hLines = [ + "// AUTOGENERATED by scripts/genGnoKernel.py — DO NOT EDIT.", + "// GNO kernel toolset $03 wrappers, callable from C.", + "// Convention: each K* returns the kernel result (or -1 on error);", + "// the last argument is `int *errno` and gets the kernel's errno.", + "//", + "// These are LOW-LEVEL primitives — libc routines in libcGno.c", + "// wrap them into POSIX-named fork/exec/wait/etc.", + "#ifndef GNO_KERNEL_H", + "#define GNO_KERNEL_H", + "", + "#include ", + "", + ] + for funcId, name, retSize, argSizes in SYSCALLS: + # Skip the sanity-check duplicate. + if name.endswith("_dup"): + continue + retC = "void" if retSize == 0 else ( + "int" if retSize == 2 else "unsigned long") + argC = [] + for i, size in enumerate(argSizes): + t = "int" if size == 2 else "void *" + argC.append(f"{t} a{i}") + hLines.append(f"extern {retC} {name}({', '.join(argC) or 'void'});" + f" // 0x{funcId:04X}") + hLines.append("") + hLines.append("#endif") + hLines.append("") + return hLines + + +def main(): + asmLines = [ + "; AUTOGENERATED by scripts/genGnoKernel.py — DO NOT EDIT by hand.", + ";", + "; GNO/ME kernel toolset $03 wrappers.", + "; Dispatcher: JSL $E10000 with LDX #funcId.", + ";", + "; C ABI: arg0 (i16) in A, arg0 (i32) in A:X, arg1+ on stack", + "; (caller-pushed, lo-word first for 32-bit values).", + "; Each wrapper re-pushes args in Pascal order (high-word first", + "; for 32-bit), allocates result space, dispatches, pops result.", + "", + "\t.text", + "", + ] + seen = set() + for funcId, name, retSize, argSizes in SYSCALLS: + if name in seen: + continue + seen.add(name) + asmLines.extend(emitWrapper(funcId, name, retSize, argSizes)) + + OUT_ASM.parent.mkdir(parents=True, exist_ok=True) + OUT_ASM.write_text("\n".join(asmLines)) + print(f"wrote {OUT_ASM} ({len(asmLines)} lines, " + f"{len(seen) - 1} wrappers)") + + OUT_HEADER.parent.mkdir(parents=True, exist_ok=True) + OUT_HEADER.write_text("\n".join(emitHeader())) + print(f"wrote {OUT_HEADER}") + + +if __name__ == "__main__": + main() diff --git a/scripts/runInGno.sh b/scripts/runInGno.sh new file mode 100755 index 0000000..95ca90a --- /dev/null +++ b/scripts/runInGno.sh @@ -0,0 +1,171 @@ +#!/usr/bin/env bash +# runInGno.sh — run a llvm816-built shell command under real GNO/ME in MAME. +# +# Boots GS/OS 6.0.4 + GNO/ME 2.0.6, logs in as root, runs the given +# program at the gsh prompt, and polls bank-2 memory for a signature. +# +# Usage: +# runInGno.sh [--check addr=hexval ...] [--snapshots] +# +# a GS/OS-loadable OMF (NOT a flat link816 .bin). +# Build it through omfEmit — see demos/buildGno.sh. +# --check A=V after the program runs, assert mem[A] (16-bit) == V. +# --snapshots save PNGs of each boot/login/run stage to +# $GNO_SNAP_DIR (default /tmp/gnosnaps) for headless +# debugging. +# +# The program is placed on the disk as /bin/ (lowercased) and as +# /HELLO, so it can be invoked by bare name at the gsh prompt. +# +# Boot timeline (frame numbers tuned against MAME apple2gs; override +# via the GNO_*_FRAMES env vars if your MAME build differs): +# ~3600 GS/OS Finder ready, GNO + System Disk icons visible +# 3800 select GNO volume, Open Apple+O to open it +# 4600 select KERN, Open Apple+O to launch the GNO kernel +# ~16000 getty shows "login:" prompt +# 16200 type "root\n" (root has empty password) +# ~18000 gsh "% " prompt +# 18800 type "\n" to run the program +# 19500+ poll bank-2 markers + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +PROG="${1:-}" +shift || true +[ -n "$PROG" ] || { echo "usage: $0 [--check A=V ...] [--snapshots]" >&2; exit 2; } +[ -f "$PROG" ] || { echo "program not found: $PROG" >&2; exit 2; } + +CADIUS="$ROOT/tools/cadius/cadius" +SYSDISK="$ROOT/tools/gsos/6.0.4 - System.Disk.po" +BASE="$ROOT/tools/gno/gnobase.po" + +[ -x "$CADIUS" ] || { echo "cadius missing" >&2; exit 1; } +[ -f "$SYSDISK" ] || { echo "GS/OS System Disk missing: $SYSDISK" >&2; exit 1; } +[ -f "$BASE" ] || { echo "GNO base disk missing — run tools/gno/buildDisk.sh" >&2; exit 1; } + +SNAPSHOTS=0 +CHECK_LUA="" +ADDR_LIST=(); EXPECT_LIST=() +while [ $# -gt 0 ]; do + case "$1" in + --snapshots) SNAPSHOTS=1; shift;; + --check) + shift + while [ $# -gt 0 ] && [[ "$1" != "--"* ]]; do + a="${1%=*}"; v="${1#*=}" + ADDR_LIST+=("$a"); EXPECT_LIST+=("$v") + shift + done;; + *) echo "unknown arg: $1" >&2; exit 2;; + esac +done + +WORK=$(mktemp -d -t gno-run.XXXXXX) +trap 'rm -rf "$WORK"' EXIT +DATA="$WORK/data.po" +cp "$BASE" "$DATA" + +NAME=$(basename "$PROG" | sed 's/\.[^.]*$//' | tr '[:upper:]' '[:lower:]' | cut -c1-15) +cp "$PROG" "$WORK/${NAME}#B50100" +cp "$PROG" "$WORK/HELLO#B50100" +"$CADIUS" ADDFILE "$DATA" /GNO.BOOT/bin "$WORK/${NAME}#B50100" >/dev/null +"$CADIUS" ADDFILE "$DATA" /GNO.BOOT "$WORK/HELLO#B50100" >/dev/null +# Optional: drop an extra TXT file in /home/root (the login cwd) for +# stdin-redirect tests. Reference it relatively or as /home/root/. +if [ -n "${GNO_ADDFILE:-}" ] && [ -f "${GNO_ADDFILE}" ]; then + cp "${GNO_ADDFILE}" "$WORK/$(basename "${GNO_ADDFILE}")#040000" + "$CADIUS" ADDFILE "$DATA" /GNO.BOOT/home/root "$WORK/$(basename "${GNO_ADDFILE}")#040000" >/dev/null +fi + +SNAPDIR="${GNO_SNAP_DIR:-/tmp/gnosnaps}" +if [ "$SNAPSHOTS" = 1 ]; then + mkdir -p "$SNAPDIR"; rm -rf "$SNAPDIR/apple2gs"/* 2>/dev/null || true +fi + +# Frame tuning. +F_VOL=${GNO_VOL_FRAMES:-3800} +F_KERN=${GNO_KERN_FRAMES:-4600} +F_LOGIN=${GNO_LOGIN_FRAMES:-16200} +F_RUN=${GNO_RUN_FRAMES:-18800} +F_POLL=${GNO_POLL_FRAMES:-20000} +F_END=${GNO_END_FRAMES:-21000} +SECS=${MAME_SECS:-380} + +# Optional: inject a line into the console after the program launches +# (for testing interactive stdin reads). GNO_STDIN is the text; it is +# posted with a trailing newline at frame F_RUN + GNO_STDIN_DELAY. +STDIN_STEP="" +if [ -n "${GNO_STDIN:-}" ]; then + STDIN_STEP="{$((F_RUN + ${GNO_STDIN_DELAY:-1600})), function() nat:post(\"${GNO_STDIN}\\n\") end}," +fi + +# Build the bank-2 probe Lua for each --check. +for i in "${!ADDR_LIST[@]}"; do + CHECK_LUA="$CHECK_LUA print(string.format('MAME-READ addr=0x%06x val=0x%04x', ${ADDR_LIST[$i]}, mem:read_u16(${ADDR_LIST[$i]})))"$'\n' +done + +LUA="$WORK/gno.lua" +cat > "$LUA" <= steps[idx][1] do steps[idx][2](); idx = idx + 1 end + if frame == $F_POLL then + local mem = manager.machine.devices[":maincpu"].spaces["program"] +$CHECK_LUA + end + if frame > $F_END then manager.machine:exit() end +end) +EOF + +OUT=$(SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy timeout "${MAME_WALL:-120}" mame apple2gs \ + -rompath "$ROOT/tools/mame/roms" \ + -flop3 "$SYSDISK" -flop4 "$DATA" \ + -snapshot_directory "$SNAPDIR" \ + -autoboot_script "$LUA" \ + -video none -sound none -nothrottle -seconds_to_run "$SECS" 2>&1) + +echo "$OUT" | grep -E "^MAME-" || true + +rc=0 +for i in "${!ADDR_LIST[@]}"; do + a="${ADDR_LIST[$i]}"; exp="${EXPECT_LIST[$i]}" + got=$(echo "$OUT" | awk -v a="$a" 'index($0, "addr=" a) { print $NF }' | tail -1) + got="${got#val=}" + want="0x$(printf '%04x' "0x$exp")" + if [ "$got" = "$want" ]; then + echo "[llvm816] GNO check OK: $a = $got" + else + echo "[llvm816 FAIL] GNO check $a: expected $want got ${got:-(none)}" + rc=1 + fi +done +exit $rc diff --git a/src/link816/link816.cpp b/src/link816/link816.cpp index d25517f..9fa595a 100644 --- a/src/link816/link816.cpp +++ b/src/link816/link816.cpp @@ -363,7 +363,14 @@ static void applyReloc(std::vector &buf, uint32_t off, if (gRecordSites) { uint32_t targetBank = target & 0xFF0000; uint32_t baseBank = gTextBaseForSites & 0xFF0000; - if (targetBank == baseBank) { + // A target below the text base is never an intra-segment + // relocatable site: it is an undefined-weak symbol (resolveSym + // resolves those to 0) or an absolute address. Recording a + // cRELOC for it would (a) underflow offsetRef = target - textBase + // (omfEmit rejects it as out-of-range) and (b) make the Loader + // rewrite a genuine null to segPlacedBase, breaking the + // `if (weakFn) weakFn()` null test that the null is meant to fail. + if (targetBank == baseBank && target >= gTextBaseForSites) { Imm24Site s; s.patchOff = patchAddr - gTextBaseForSites; s.offsetRef = target - gTextBaseForSites; @@ -386,7 +393,9 @@ static void applyReloc(std::vector &buf, uint32_t off, if (gRecordSites) { uint32_t targetBank = target & 0xFF0000; uint32_t baseBank = gTextBaseForSites & 0xFF0000; - if (targetBank == baseBank) { + // See R_W65816_IMM16: skip undefined-weak/absolute targets + // below the text base (no valid intra-segment cRELOC). + if (targetBank == baseBank && target >= gTextBaseForSites) { Imm24Site s; s.patchOff = patchAddr - gTextBaseForSites; s.offsetRef = target - gTextBaseForSites; @@ -413,7 +422,9 @@ static void applyReloc(std::vector &buf, uint32_t off, // and shouldn't be relocated by the Loader. uint32_t targetBank = target & 0xFF0000; uint32_t baseBank = gTextBaseForSites & 0xFF0000; - if (targetBank == baseBank) { + // See R_W65816_IMM16: skip undefined-weak/absolute targets + // below the text base (no valid intra-segment cRELOC). + if (targetBank == baseBank && target >= gTextBaseForSites) { Imm24Site s; s.patchOff = patchAddr - gTextBaseForSites; s.offsetRef = target - gTextBaseForSites; @@ -464,6 +475,15 @@ struct Linker { uint32_t segmentCap = 0; uint32_t segmentBankBase = 0x040000; std::string manifestPath; + // ProDOS file metadata for the resulting OMF. Set via --filetype / + // --aux. Used by disk-image builders (e.g. cppo, Cadius) to set + // the on-disk filetype/aux when copying the .bin onto a 2mg. + // For GS/OS apps: filetype=$B3 (S16), aux=0. + // For GNO shell commands: filetype=$B5 (EXE), aux=0 (or $DC00 for + // non-compliant programs that bypass GNO's keyboard buffer). + // -1 sentinel = "not set" (caller hasn't asked for a sidecar). + int32_t fileType = -1; + int32_t auxType = -1; // Per-section identity: (object index, section index within obj). using SecID = std::pair; @@ -1433,12 +1453,20 @@ static void usage(const char *argv0) { "usage: %s -o [--text-base ADDR] [--rodata-base ADDR]\n" " [--bss-base ADDR] [--map FILE] [--debug-out FILE]\n" " [--reloc-out FILE] [--no-gc-sections]\n" + " [--filetype N] [--aux N]\n" " ...\n" "\n" " --reloc-out FILE write IMM24 relocation site list (binary:\n" " ...)\n" " consumed by omfEmit --relocs to emit cRELOC\n" - " opcodes for runtime bank-byte fixup.\n", + " opcodes for runtime bank-byte fixup.\n" + " --filetype N ProDOS filetype (0..0xFF) for the OMF. $B3=S16\n" + " (GS/OS app), $B5=EXE (GNO shell command). Writes\n" + " .meta sidecar consumed by disk-image\n" + " builders.\n" + " --aux N ProDOS auxtype (0..0xFFFF). Typically 0 for\n" + " compliant programs; $DC00 = GNO non-compliant\n" + " (bypasses interrupt-driven keyboard buffer).\n", argv0); std::exit(2); } @@ -1497,6 +1525,16 @@ int main(int argc, char **argv) { } else if (a == "--manifest") { if (++i >= argc) usage(argv[0]); linker.manifestPath = argv[i++]; + } else if (a == "--filetype") { + if (++i >= argc) usage(argv[0]); + linker.fileType = (int32_t)parseInt(argv[i++]); + if (linker.fileType < 0 || linker.fileType > 0xFF) + die("--filetype must be 0..0xFF"); + } else if (a == "--aux") { + if (++i >= argc) usage(argv[0]); + linker.auxType = (int32_t)parseInt(argv[i++]); + if (linker.auxType < 0 || linker.auxType > 0xFFFF) + die("--aux must be 0..0xFFFF"); } else if (a == "-h" || a == "--help") { usage(argv[0]); } else if (!a.empty() && a[0] == '-') { @@ -1523,6 +1561,21 @@ int main(int argc, char **argv) { if (!f) die("cannot open '" + outPath + "' for writing"); f.write(reinterpret_cast(image.data()), image.size()); + // ProDOS filetype/aux sidecar. Emit as `.meta` — simple + // line-oriented `key: 0xVALUE` format consumed by the disk-image + // builder (cppo/Cadius/our scripts). Default filetype if not + // specified: 0xB3 (S16) for single-segment, the multi-segment + // builder writes 0xB3 anyway. + if (linker.fileType >= 0 || linker.auxType >= 0) { + std::string metaPath = outPath + ".meta"; + std::ofstream mf(metaPath); + if (!mf) die("cannot open '" + metaPath + "' for writing"); + if (linker.fileType >= 0) + mf << "filetype: 0x" << std::hex << linker.fileType << "\n"; + if (linker.auxType >= 0) + mf << "aux: 0x" << std::hex << linker.auxType << "\n"; + } + if (!mapPath.empty()) linker.writeMap(mapPath); if (!debugOutPath.empty()) linker.writeDebugSidecar(debugOutPath); if (!relocOutPath.empty()) { diff --git a/src/llvm/lib/Target/W65816/W65816AsmPrinter.cpp b/src/llvm/lib/Target/W65816/W65816AsmPrinter.cpp index f063a4b..502da4d 100644 --- a/src/llvm/lib/Target/W65816/W65816AsmPrinter.cpp +++ b/src/llvm/lib/Target/W65816/W65816AsmPrinter.cpp @@ -18,6 +18,7 @@ #include "TargetInfo/W65816TargetInfo.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineInstr.h" +#include "llvm/IR/GlobalValue.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" @@ -311,12 +312,30 @@ void W65816AsmPrinter::emitInstruction(const MachineInstr *MI) { // first; the Loader's cRELOC `BitShift=16` mechanism doesn't track // the placed bank (segPlacedBase appears 16-bit-only in the formula // it actually uses). `lda $BE` is also 2 bytes vs 3 for `lda #imm` - // so this is a size win too. The symbol operand on the pseudo is - // ignored — only the SDNode's value-type matters, which is i16 - // bank|pad. + // so this is a size win too. + // + // EXCEPTION — external-weak symbols: a `&weakfn` whose definition may + // be absent at link time must be representable as NULL. If we emit + // `lda $BE` for its bank, the runtime PBR (non-zero when GS/OS loads + // the segment in a non-zero bank) makes the pointer always non-null, + // so `if (weakfn) weakfn()` is always taken and jumps to the + // unresolved (=0) target — a crash. For an external-weak global the + // bank must be a literal 0 instead, so an undefined-weak ref is a + // genuine null and a strong link-time def still works (its low16 is + // non-zero so the null test passes and the direct call uses the + // symbol's own reloc, not this bank byte). Defined/normal globals + // and compiler external symbols (libgcc helpers) keep `lda $BE`. + const MachineOperand &Sym = MI->getOperand(1); + bool WeakUndef = Sym.isGlobal() && + Sym.getGlobal()->hasExternalWeakLinkage(); MCInst Lda; - Lda.setOpcode(W65816::LDA_DP); - Lda.addOperand(MCOperand::createImm(0xBE)); + if (WeakUndef) { + Lda.setOpcode(W65816::LDA_Imm16); + Lda.addOperand(MCOperand::createImm(0)); + } else { + Lda.setOpcode(W65816::LDA_DP); + Lda.addOperand(MCOperand::createImm(0xBE)); + } EmitToStreamer(*OutStreamer, Lda); return; } diff --git a/src/llvm/lib/Target/W65816/W65816StackSlotCleanup.cpp b/src/llvm/lib/Target/W65816/W65816StackSlotCleanup.cpp index 0e41f9d..51accc8 100644 --- a/src/llvm/lib/Target/W65816/W65816StackSlotCleanup.cpp +++ b/src/llvm/lib/Target/W65816/W65816StackSlotCleanup.cpp @@ -884,6 +884,14 @@ bool W65816StackSlotCleanup::runOnMachineFunction(MachineFunction &MF) { // real test and a flag-using branch. return Opc == W65816::LDAi16imm || Opc == W65816::LDAi8imm || + // LDAi16imm_bank lowers to `lda $BE` (LDA_DP of the program- + // bank byte). Under GNO/ME the program bank is non-zero, so + // this load sets N/Z != the CMP's result. Between a CMP and a + // BEQ/BNE (e.g. the bank half of an i32 `select`/`if(!p)` + // pointer pick), it corrupts the test and the wrong half is + // selected -- the `%s`-of-stack-string garbling bug. Plain + // LDAi16imm was already listed; its _bank sibling must be too. + Opc == W65816::LDAi16imm_bank || Opc == W65816::LDXi16imm || Opc == W65816::LDA_StackRel || Opc == W65816::LDA_StackRelIndY ||