GNO Support

This commit is contained in:
Scott Duensing 2026-05-29 15:43:28 -05:00
parent f4503f73c0
commit 9e53e5fd38
55 changed files with 3490 additions and 389 deletions

3
.gitignore vendored
View file

@ -20,3 +20,6 @@ tests/coremark/coreMark.bin
*.swo
.DS_Store
*~
stuff/
ROM Source Code.zip

59
demos/buildGno.sh Executable file
View file

@ -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 <basename>
# demos/<basename>.c -> demos/<basename>.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 <basename>" >&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)"

BIN
demos/gnoCat.bin Normal file

Binary file not shown.

15
demos/gnoCat.c Normal file
View file

@ -0,0 +1,15 @@
// gnoCat.c — read one line from stdin (GNO console) and echo it.
#include <stdint.h>
#include <stdio.h>
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;
}

221
demos/gnoCat.map Normal file
View file

@ -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

BIN
demos/gnoCat.o Normal file

Binary file not shown.

BIN
demos/gnoCat.omf Normal file

Binary file not shown.

BIN
demos/gnoCat.reloc Normal file

Binary file not shown.

BIN
demos/gnoFile.bin Normal file

Binary file not shown.

31
demos/gnoFile.c Normal file
View file

@ -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 <stdint.h>
#include <stdio.h>
#include <string.h>
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;
}

269
demos/gnoFile.map Normal file
View file

@ -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

BIN
demos/gnoFile.o Normal file

Binary file not shown.

BIN
demos/gnoFile.omf Normal file

Binary file not shown.

BIN
demos/gnoFile.reloc Normal file

Binary file not shown.

BIN
demos/gnoFmt.bin Normal file

Binary file not shown.

21
demos/gnoFmt.c Normal file
View file

@ -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 <stdint.h>
#include <stdio.h>
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;
}

203
demos/gnoFmt.map Normal file
View file

@ -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

BIN
demos/gnoFmt.o Normal file

Binary file not shown.

BIN
demos/gnoFmt.omf Normal file

Binary file not shown.

BIN
demos/gnoFmt.reloc Normal file

Binary file not shown.

BIN
demos/gnoHello.bin Normal file

Binary file not shown.

14
demos/gnoHello.c Normal file
View file

@ -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 <stdint.h>
#include <stdio.h>
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;
}

203
demos/gnoHello.map Normal file
View file

@ -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

BIN
demos/gnoHello.o Normal file

Binary file not shown.

BIN
demos/gnoHello.omf Normal file

Binary file not shown.

BIN
demos/gnoHello.reloc Normal file

Binary file not shown.

BIN
demos/gnoStdin.bin Normal file

Binary file not shown.

23
demos/gnoStdin.c Normal file
View file

@ -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 <stdint.h>
#include <stdio.h>
#include <string.h>
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;
}

201
demos/gnoStdin.map Normal file
View file

@ -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

BIN
demos/gnoStdin.o Normal file

Binary file not shown.

BIN
demos/gnoStdin.omf Normal file

Binary file not shown.

BIN
demos/gnoStdin.reloc Normal file

Binary file not shown.

Binary file not shown.

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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

Binary file not shown.

Binary file not shown.

View file

@ -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"

View file

@ -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 <stdint.h>
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

View file

@ -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

141
runtime/src/crt0Gno.s Normal file
View file

@ -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

View file

@ -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
; 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"

42
runtime/src/gnoGsos.s Normal file
View file

@ -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

852
runtime/src/gnoKernel.s Normal file
View file

@ -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

View file

@ -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,7 +228,15 @@ 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) {
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;

295
runtime/src/libcGno.c Normal file
View file

@ -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 <stdint.h>
#include <gno/kernel.h>
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);
}

View file

@ -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++;

236
scripts/genGnoKernel.py Normal file
View file

@ -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 <stdint.h>",
"",
]
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()

171
scripts/runInGno.sh Executable file
View file

@ -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 <program.omf> [--check addr=hexval ...] [--snapshots]
#
# <program.omf> 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/<name> (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 "<name>\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 <program.omf> [--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/<name>.
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" <<EOF
local nat = manager.machine.natkeyboard
local frame = 0
local idx = 1
local function gf(p, n) local x = manager.machine.ioport.ports[p]; return x and x.fields[n] end
local key_cmd = gf(":macadb:KEY3", "Command / Open Apple")
local function press(f) if f then f:set_value(1) end end
local function release(f) if f then f:set_value(0) end end
local snaps = $SNAPSHOTS
local function snap(t) if snaps == 1 then manager.machine.video:snapshot() end; print("MAME-STAGE "..t.." frame="..frame) end
local steps = {
{$F_VOL, function() nat:post("G") end},
{$F_VOL + 200, function() press(key_cmd) end},
{$F_VOL + 206, function() nat:post("o") end},
{$F_VOL + 260, function() release(key_cmd) end},
{$F_VOL + 400, function() snap("vol-open") end},
{$F_KERN, function() nat:post("K") end},
{$F_KERN + 200, function() press(key_cmd) end},
{$F_KERN + 206, function() nat:post("o") end},
{$F_KERN + 260, function() release(key_cmd) end},
{$F_LOGIN - 200,function() snap("login") end},
{$F_LOGIN, function() nat:post("root\n") end},
{$F_RUN - 200, function() snap("shell") end},
{$F_RUN, function() nat:post("${GNO_RUNCMD:-$NAME}\n") end},
${STDIN_STEP}
{$F_POLL, function() snap("running") end},
}
emu.register_frame_done(function()
frame = frame + 1
while idx <= #steps and frame >= 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

View file

@ -363,7 +363,14 @@ static void applyReloc(std::vector<uint8_t> &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<uint8_t> &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<uint8_t> &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<size_t, uint32_t>;
@ -1433,12 +1453,20 @@ static void usage(const char *argv0) {
"usage: %s -o <output> [--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"
" <input.o> ...\n"
"\n"
" --reloc-out FILE write IMM24 relocation site list (binary:\n"
" <count:u32><patchOff:u32 offsetRef:u32>...)\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"
" <output>.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<const char *>(image.data()), image.size());
// ProDOS filetype/aux sidecar. Emit as `<output>.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()) {

View file

@ -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;
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;
}

View file

@ -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 ||