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 *.swo
.DS_Store .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 # section layout
.text : 0x001000 .. 0x001c3d ( 3133 bytes) .text : 0x001000 .. 0x001be6 ( 3046 bytes)
.rodata : 0x001c3d .. 0x001c51 ( 20 bytes) .rodata : 0x001be6 .. 0x001be6 ( 0 bytes)
.bss : 0x00a000 .. 0x00a002 ( 2 bytes) .bss : 0x00a000 .. 0x00a002 ( 2 bytes)
# per-input-file .text contributions # 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 395 /home/scott/claude/llvm816/demos/helloBeep.o
30853 /home/scott/claude/llvm816/runtime/libc.o 32173 /home/scott/claude/llvm816/runtime/libc.o
9098 /home/scott/claude/llvm816/runtime/snprintf.o 9075 /home/scott/claude/llvm816/runtime/snprintf.o
10865 /home/scott/claude/llvm816/runtime/extras.o 10814 /home/scott/claude/llvm816/runtime/extras.o
4374 /home/scott/claude/llvm816/runtime/softFloat.o 4364 /home/scott/claude/llvm816/runtime/softFloat.o
13388 /home/scott/claude/llvm816/runtime/softDouble.o 13051 /home/scott/claude/llvm816/runtime/softDouble.o
176 /home/scott/claude/llvm816/runtime/iigsGsos.o 176 /home/scott/claude/llvm816/runtime/iigsGsos.o
20670 /home/scott/claude/llvm816/runtime/iigsToolbox.o 20670 /home/scott/claude/llvm816/runtime/iigsToolbox.o
1139 /home/scott/claude/llvm816/runtime/desktop.o 1139 /home/scott/claude/llvm816/runtime/desktop.o
@ -32,55 +32,54 @@
0x000002 __bss_size 0x000002 __bss_size
0x001000 __start 0x001000 __start
0x001000 __text_start 0x001000 __text_start
0x0010ba main 0x001063 main
0x001245 __jsl_indir 0x0011ee __jsl_indir
0x001248 __mulhi3 0x0011f1 __mulhi3
0x001267 __umulhisi3 0x001210 __umulhisi3
0x0012be __ashlhi3 0x001267 __ashlhi3
0x0012cd __lshrhi3 0x001276 __lshrhi3
0x0012dd __ashrhi3 0x001286 __ashrhi3
0x0012f0 __udivhi3 0x001299 __udivhi3
0x0012fc __umodhi3 0x0012a5 __umodhi3
0x001308 __divhi3 0x0012b1 __divhi3
0x001322 __modhi3 0x0012cb __modhi3
0x00133c __divmod_setup 0x0012e5 __divmod_setup
0x00136f __udivmod_core 0x001318 __udivmod_core
0x00138d __mulsi3 0x001336 __mulsi3
0x001446 __ashlsi3 0x0013ef __ashlsi3
0x00145b __lshrsi3 0x001404 __lshrsi3
0x001470 __ashrsi3 0x001419 __ashrsi3
0x00148a __udivmodsi_core 0x001433 __udivmodsi_core
0x0014c2 __udivsi3 0x00146b __udivsi3
0x0014d6 __umodsi3 0x00147f __umodsi3
0x0014ea __divsi3 0x001493 __divsi3
0x001511 __modsi3 0x0014ba __modsi3
0x001538 __divmodsi_setup 0x0014e1 __divmodsi_setup
0x001589 __divmoddi4_stash 0x001532 __divmoddi4_stash
0x0015a6 __retdi 0x00154f __retdi
0x0015b3 __ashldi3 0x00155c __ashldi3
0x0015d6 __lshrdi3 0x00157f __lshrdi3
0x0015f9 __ashrdi3 0x0015a2 __ashrdi3
0x00161f __muldi3 0x0015c8 __muldi3
0x001686 __ucmpdi2 0x00162f __ucmpdi2
0x0016af __cmpdi2 0x001658 __cmpdi2
0x0016e6 __udivdi3 0x00168f __udivdi3
0x0016ef __umoddi3 0x001698 __umoddi3
0x001708 __udivmoddi_core 0x0016b1 __udivmoddi_core
0x001755 __divdi3 0x0016fe __divdi3
0x001774 __moddi3 0x00171d __moddi3
0x0017a1 __absdi_a 0x00174a __absdi_a
0x0017a9 __absdi_b 0x001752 __absdi_b
0x0017b1 __negdi_a 0x00175a __negdi_a
0x0017cf __negdi_b 0x001778 __negdi_b
0x0017ed setjmp 0x001796 setjmp
0x001815 longjmp 0x0017be longjmp
0x00183f __umulhisi3_qsq 0x0017e8 __umulhisi3_qsq
0x001c3d __rodata_start 0x001be6 __init_array_end
0x001c3d __text_end 0x001be6 __init_array_start
0x001c3d gChainPath 0x001be6 __rodata_end
0x001c51 __init_array_end 0x001be6 __rodata_start
0x001c51 __init_array_start 0x001be6 __text_end
0x001c51 __rodata_end
0x00a000 __bss_lo16 0x00a000 __bss_lo16
0x00a000 __bss_seg0_lo16 0x00a000 __bss_seg0_lo16
0x00a000 __bss_start 0x00a000 __bss_start
@ -88,14 +87,14 @@
0x00a002 __bss_end 0x00a002 __bss_end
0x00a002 __heap_start 0x00a002 __heap_start
0x00bf00 __heap_end 0x00bf00 __heap_end
__absdi_a = 0x0017a1 __absdi_a = 0x00174a
__absdi_b = 0x0017a9 __absdi_b = 0x001752
__ashldi3 = 0x0015b3 __ashldi3 = 0x00155c
__ashlhi3 = 0x0012be __ashlhi3 = 0x001267
__ashlsi3 = 0x001446 __ashlsi3 = 0x0013ef
__ashrdi3 = 0x0015f9 __ashrdi3 = 0x0015a2
__ashrhi3 = 0x0012dd __ashrhi3 = 0x001286
__ashrsi3 = 0x001470 __ashrsi3 = 0x001419
__bss_bank = 0x000000 __bss_bank = 0x000000
__bss_end = 0x00a002 __bss_end = 0x00a002
__bss_lo16 = 0x00a000 __bss_lo16 = 0x00a000
@ -113,49 +112,48 @@ __bss_seg3_lo16 = 0x000000
__bss_seg3_size = 0x000000 __bss_seg3_size = 0x000000
__bss_size = 0x000002 __bss_size = 0x000002
__bss_start = 0x00a000 __bss_start = 0x00a000
__cmpdi2 = 0x0016af __cmpdi2 = 0x001658
__divdi3 = 0x001755 __divdi3 = 0x0016fe
__divhi3 = 0x001308 __divhi3 = 0x0012b1
__divmod_setup = 0x00133c __divmod_setup = 0x0012e5
__divmoddi4_stash = 0x001589 __divmoddi4_stash = 0x001532
__divmodsi_setup = 0x001538 __divmodsi_setup = 0x0014e1
__divsi3 = 0x0014ea __divsi3 = 0x001493
__heap_end = 0x00bf00 __heap_end = 0x00bf00
__heap_start = 0x00a002 __heap_start = 0x00a002
__indirTarget = 0x00a000 __indirTarget = 0x00a000
__init_array_end = 0x001c51 __init_array_end = 0x001be6
__init_array_start = 0x001c51 __init_array_start = 0x001be6
__jsl_indir = 0x001245 __jsl_indir = 0x0011ee
__lshrdi3 = 0x0015d6 __lshrdi3 = 0x00157f
__lshrhi3 = 0x0012cd __lshrhi3 = 0x001276
__lshrsi3 = 0x00145b __lshrsi3 = 0x001404
__moddi3 = 0x001774 __moddi3 = 0x00171d
__modhi3 = 0x001322 __modhi3 = 0x0012cb
__modsi3 = 0x001511 __modsi3 = 0x0014ba
__muldi3 = 0x00161f __muldi3 = 0x0015c8
__mulhi3 = 0x001248 __mulhi3 = 0x0011f1
__mulsi3 = 0x00138d __mulsi3 = 0x001336
__negdi_a = 0x0017b1 __negdi_a = 0x00175a
__negdi_b = 0x0017cf __negdi_b = 0x001778
__retdi = 0x0015a6 __retdi = 0x00154f
__rodata_end = 0x001c51 __rodata_end = 0x001be6
__rodata_start = 0x001c3d __rodata_start = 0x001be6
__start = 0x001000 __start = 0x001000
__text_end = 0x001c3d __text_end = 0x001be6
__text_start = 0x001000 __text_start = 0x001000
__ucmpdi2 = 0x001686 __ucmpdi2 = 0x00162f
__udivdi3 = 0x0016e6 __udivdi3 = 0x00168f
__udivhi3 = 0x0012f0 __udivhi3 = 0x001299
__udivmod_core = 0x00136f __udivmod_core = 0x001318
__udivmoddi_core = 0x001708 __udivmoddi_core = 0x0016b1
__udivmodsi_core = 0x00148a __udivmodsi_core = 0x001433
__udivsi3 = 0x0014c2 __udivsi3 = 0x00146b
__umoddi3 = 0x0016ef __umoddi3 = 0x001698
__umodhi3 = 0x0012fc __umodhi3 = 0x0012a5
__umodsi3 = 0x0014d6 __umodsi3 = 0x00147f
__umulhisi3 = 0x001267 __umulhisi3 = 0x001210
__umulhisi3_qsq = 0x00183f __umulhisi3_qsq = 0x0017e8
gChainPath = 0x001c3d longjmp = 0x0017be
longjmp = 0x001815 main = 0x001063
main = 0x0010ba setjmp = 0x001796
setjmp = 0x0017ed

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,16 +1,16 @@
# section layout # section layout
.text : 0x001000 .. 0x002108 ( 4360 bytes) .text : 0x001000 .. 0x0020b1 ( 4273 bytes)
.rodata : 0x002108 .. 0x002176 ( 110 bytes) .rodata : 0x0020b1 .. 0x00210b ( 90 bytes)
.bss : 0x00a000 .. 0x00a00a ( 10 bytes) .bss : 0x00a000 .. 0x00a00a ( 10 bytes)
# per-input-file .text contributions # 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 546 /home/scott/claude/llvm816/demos/helloText.o
30853 /home/scott/claude/llvm816/runtime/libc.o 32173 /home/scott/claude/llvm816/runtime/libc.o
9098 /home/scott/claude/llvm816/runtime/snprintf.o 9075 /home/scott/claude/llvm816/runtime/snprintf.o
10865 /home/scott/claude/llvm816/runtime/extras.o 10814 /home/scott/claude/llvm816/runtime/extras.o
4374 /home/scott/claude/llvm816/runtime/softFloat.o 4364 /home/scott/claude/llvm816/runtime/softFloat.o
13388 /home/scott/claude/llvm816/runtime/softDouble.o 13051 /home/scott/claude/llvm816/runtime/softDouble.o
176 /home/scott/claude/llvm816/runtime/iigsGsos.o 176 /home/scott/claude/llvm816/runtime/iigsGsos.o
20670 /home/scott/claude/llvm816/runtime/iigsToolbox.o 20670 /home/scott/claude/llvm816/runtime/iigsToolbox.o
1139 /home/scott/claude/llvm816/runtime/desktop.o 1139 /home/scott/claude/llvm816/runtime/desktop.o
@ -32,71 +32,70 @@
0x00000a __bss_size 0x00000a __bss_size
0x001000 __start 0x001000 __start
0x001000 __text_start 0x001000 __text_start
0x0010ba main 0x001063 main
0x0012dc CtlStartUp 0x001285 CtlStartUp
0x0012ec EMStartUp 0x001295 EMStartUp
0x00130b GetNextEvent 0x0012b4 GetNextEvent
0x001322 FMStartUp 0x0012cb FMStartUp
0x001332 LEStartUp 0x0012db LEStartUp
0x001342 LoadOneTool 0x0012eb LoadOneTool
0x001352 NewHandle 0x0012fb NewHandle
0x001378 MenuStartUp 0x001321 MenuStartUp
0x001388 QDStartUp 0x001331 QDStartUp
0x00139e DrawString 0x001347 DrawString
0x0013b0 MoveTo 0x001359 MoveTo
0x0013c0 startdesk 0x001369 startdesk
0x0016de paintDesktopBackdrop 0x001687 paintDesktopBackdrop
0x001710 __jsl_indir 0x0016b9 __jsl_indir
0x001713 __mulhi3 0x0016bc __mulhi3
0x001732 __umulhisi3 0x0016db __umulhisi3
0x001789 __ashlhi3 0x001732 __ashlhi3
0x001798 __lshrhi3 0x001741 __lshrhi3
0x0017a8 __ashrhi3 0x001751 __ashrhi3
0x0017bb __udivhi3 0x001764 __udivhi3
0x0017c7 __umodhi3 0x001770 __umodhi3
0x0017d3 __divhi3 0x00177c __divhi3
0x0017ed __modhi3 0x001796 __modhi3
0x001807 __divmod_setup 0x0017b0 __divmod_setup
0x00183a __udivmod_core 0x0017e3 __udivmod_core
0x001858 __mulsi3 0x001801 __mulsi3
0x001911 __ashlsi3 0x0018ba __ashlsi3
0x001926 __lshrsi3 0x0018cf __lshrsi3
0x00193b __ashrsi3 0x0018e4 __ashrsi3
0x001955 __udivmodsi_core 0x0018fe __udivmodsi_core
0x00198d __udivsi3 0x001936 __udivsi3
0x0019a1 __umodsi3 0x00194a __umodsi3
0x0019b5 __divsi3 0x00195e __divsi3
0x0019dc __modsi3 0x001985 __modsi3
0x001a03 __divmodsi_setup 0x0019ac __divmodsi_setup
0x001a54 __divmoddi4_stash 0x0019fd __divmoddi4_stash
0x001a71 __retdi 0x001a1a __retdi
0x001a7e __ashldi3 0x001a27 __ashldi3
0x001aa1 __lshrdi3 0x001a4a __lshrdi3
0x001ac4 __ashrdi3 0x001a6d __ashrdi3
0x001aea __muldi3 0x001a93 __muldi3
0x001b51 __ucmpdi2 0x001afa __ucmpdi2
0x001b7a __cmpdi2 0x001b23 __cmpdi2
0x001bb1 __udivdi3 0x001b5a __udivdi3
0x001bba __umoddi3 0x001b63 __umoddi3
0x001bd3 __udivmoddi_core 0x001b7c __udivmoddi_core
0x001c20 __divdi3 0x001bc9 __divdi3
0x001c3f __moddi3 0x001be8 __moddi3
0x001c6c __absdi_a 0x001c15 __absdi_a
0x001c74 __absdi_b 0x001c1d __absdi_b
0x001c7c __negdi_a 0x001c25 __negdi_a
0x001c9a __negdi_b 0x001c43 __negdi_b
0x001cb8 setjmp 0x001c61 setjmp
0x001ce0 longjmp 0x001c89 longjmp
0x001d0a __umulhisi3_qsq 0x001cb3 __umulhisi3_qsq
0x002108 __rodata_start 0x0020b1 __rodata_start
0x002108 __text_end 0x0020b1 __text_end
0x002108 gChainPath 0x0020b1 line1
0x00211c line1 0x0020c6 line2
0x002131 line2 0x0020f3 line3
0x00215e line3 0x00210b __init_array_end
0x002176 __init_array_end 0x00210b __init_array_start
0x002176 __init_array_start 0x00210b __rodata_end
0x002176 __rodata_end
0x00a000 __bss_lo16 0x00a000 __bss_lo16
0x00a000 __bss_seg0_lo16 0x00a000 __bss_seg0_lo16
0x00a000 __bss_start 0x00a000 __bss_start
@ -107,25 +106,25 @@
0x00a00a __bss_end 0x00a00a __bss_end
0x00a00a __heap_start 0x00a00a __heap_start
0x00bf00 __heap_end 0x00bf00 __heap_end
CtlStartUp = 0x0012dc CtlStartUp = 0x001285
DrawString = 0x00139e DrawString = 0x001347
EMStartUp = 0x0012ec EMStartUp = 0x001295
FMStartUp = 0x001322 FMStartUp = 0x0012cb
GetNextEvent = 0x00130b GetNextEvent = 0x0012b4
LEStartUp = 0x001332 LEStartUp = 0x0012db
LoadOneTool = 0x001342 LoadOneTool = 0x0012eb
MenuStartUp = 0x001378 MenuStartUp = 0x001321
MoveTo = 0x0013b0 MoveTo = 0x001359
NewHandle = 0x001352 NewHandle = 0x0012fb
QDStartUp = 0x001388 QDStartUp = 0x001331
__absdi_a = 0x001c6c __absdi_a = 0x001c15
__absdi_b = 0x001c74 __absdi_b = 0x001c1d
__ashldi3 = 0x001a7e __ashldi3 = 0x001a27
__ashlhi3 = 0x001789 __ashlhi3 = 0x001732
__ashlsi3 = 0x001911 __ashlsi3 = 0x0018ba
__ashrdi3 = 0x001ac4 __ashrdi3 = 0x001a6d
__ashrhi3 = 0x0017a8 __ashrhi3 = 0x001751
__ashrsi3 = 0x00193b __ashrsi3 = 0x0018e4
__bss_bank = 0x000000 __bss_bank = 0x000000
__bss_end = 0x00a00a __bss_end = 0x00a00a
__bss_lo16 = 0x00a000 __bss_lo16 = 0x00a000
@ -143,57 +142,56 @@ __bss_seg3_lo16 = 0x000000
__bss_seg3_size = 0x000000 __bss_seg3_size = 0x000000
__bss_size = 0x00000a __bss_size = 0x00000a
__bss_start = 0x00a000 __bss_start = 0x00a000
__cmpdi2 = 0x001b7a __cmpdi2 = 0x001b23
__divdi3 = 0x001c20 __divdi3 = 0x001bc9
__divhi3 = 0x0017d3 __divhi3 = 0x00177c
__divmod_setup = 0x001807 __divmod_setup = 0x0017b0
__divmoddi4_stash = 0x001a54 __divmoddi4_stash = 0x0019fd
__divmodsi_setup = 0x001a03 __divmodsi_setup = 0x0019ac
__divsi3 = 0x0019b5 __divsi3 = 0x00195e
__heap_end = 0x00bf00 __heap_end = 0x00bf00
__heap_start = 0x00a00a __heap_start = 0x00a00a
__indirTarget = 0x00a008 __indirTarget = 0x00a008
__init_array_end = 0x002176 __init_array_end = 0x00210b
__init_array_start = 0x002176 __init_array_start = 0x00210b
__jsl_indir = 0x001710 __jsl_indir = 0x0016b9
__lshrdi3 = 0x001aa1 __lshrdi3 = 0x001a4a
__lshrhi3 = 0x001798 __lshrhi3 = 0x001741
__lshrsi3 = 0x001926 __lshrsi3 = 0x0018cf
__moddi3 = 0x001c3f __moddi3 = 0x001be8
__modhi3 = 0x0017ed __modhi3 = 0x001796
__modsi3 = 0x0019dc __modsi3 = 0x001985
__muldi3 = 0x001aea __muldi3 = 0x001a93
__mulhi3 = 0x001713 __mulhi3 = 0x0016bc
__mulsi3 = 0x001858 __mulsi3 = 0x001801
__negdi_a = 0x001c7c __negdi_a = 0x001c25
__negdi_b = 0x001c9a __negdi_b = 0x001c43
__retdi = 0x001a71 __retdi = 0x001a1a
__rodata_end = 0x002176 __rodata_end = 0x00210b
__rodata_start = 0x002108 __rodata_start = 0x0020b1
__start = 0x001000 __start = 0x001000
__text_end = 0x002108 __text_end = 0x0020b1
__text_start = 0x001000 __text_start = 0x001000
__ucmpdi2 = 0x001b51 __ucmpdi2 = 0x001afa
__udivdi3 = 0x001bb1 __udivdi3 = 0x001b5a
__udivhi3 = 0x0017bb __udivhi3 = 0x001764
__udivmod_core = 0x00183a __udivmod_core = 0x0017e3
__udivmoddi_core = 0x001bd3 __udivmoddi_core = 0x001b7c
__udivmodsi_core = 0x001955 __udivmodsi_core = 0x0018fe
__udivsi3 = 0x00198d __udivsi3 = 0x001936
__umoddi3 = 0x001bba __umoddi3 = 0x001b63
__umodhi3 = 0x0017c7 __umodhi3 = 0x001770
__umodsi3 = 0x0019a1 __umodsi3 = 0x00194a
__umulhisi3 = 0x001732 __umulhisi3 = 0x0016db
__umulhisi3_qsq = 0x001d0a __umulhisi3_qsq = 0x001cb3
gChainPath = 0x002108
gDpBase = 0x00a006 gDpBase = 0x00a006
gDpHandle = 0x00a002 gDpHandle = 0x00a002
gUserId = 0x00a000 gUserId = 0x00a000
line1 = 0x00211c line1 = 0x0020b1
line2 = 0x002131 line2 = 0x0020c6
line3 = 0x00215e line3 = 0x0020f3
longjmp = 0x001ce0 longjmp = 0x001c89
main = 0x0010ba main = 0x001063
paintDesktopBackdrop = 0x0016de paintDesktopBackdrop = 0x001687
setjmp = 0x001cb8 setjmp = 0x001c61
startdesk = 0x0013c0 startdesk = 0x001369

Binary file not shown.

Binary file not shown.

View file

@ -39,6 +39,10 @@ cc() {
asm "$SRC/crt0.s" asm "$SRC/crt0.s"
asm "$SRC/crt0Gsos.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" asm "$SRC/libgcc.s"
cc "$SRC/libc.c" cc "$SRC/libc.c"
cc "$SRC/strtol.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 phb ; save current DBR
; ---- segment 0 ---- ; ---- 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 rep #0x20
ldx #__bss_seg0_size ldx #__bss_seg0_size
beq .Lbss_seg1 beq .Lbss_seg1
@ -92,14 +95,11 @@ __start:
.byte __bss_seg0_bank .byte __bss_seg0_bank
pha pha
plb plb
rep #0x20
ldx #0 ldx #0
.Lbss_loop0: .Lbss_loop0:
cpx #__bss_seg0_size cpx #__bss_seg0_size
bcs .Lbss_seg1 bcs .Lbss_seg1
sep #0x20
stz __bss_seg0_lo16, x stz __bss_seg0_lo16, x
rep #0x20
inx inx
bra .Lbss_loop0 bra .Lbss_loop0
.Lbss_seg1: .Lbss_seg1:
@ -112,14 +112,11 @@ __start:
.byte __bss_seg1_bank .byte __bss_seg1_bank
pha pha
plb plb
rep #0x20
ldx #0 ldx #0
.Lbss_loop1: .Lbss_loop1:
cpx #__bss_seg1_size cpx #__bss_seg1_size
bcs .Lbss_seg2 bcs .Lbss_seg2
sep #0x20
stz __bss_seg1_lo16, x stz __bss_seg1_lo16, x
rep #0x20
inx inx
bra .Lbss_loop1 bra .Lbss_loop1
.Lbss_seg2: .Lbss_seg2:
@ -132,14 +129,11 @@ __start:
.byte __bss_seg2_bank .byte __bss_seg2_bank
pha pha
plb plb
rep #0x20
ldx #0 ldx #0
.Lbss_loop2: .Lbss_loop2:
cpx #__bss_seg2_size cpx #__bss_seg2_size
bcs .Lbss_seg3 bcs .Lbss_seg3
sep #0x20
stz __bss_seg2_lo16, x stz __bss_seg2_lo16, x
rep #0x20
inx inx
bra .Lbss_loop2 bra .Lbss_loop2
.Lbss_seg3: .Lbss_seg3:
@ -152,17 +146,15 @@ __start:
.byte __bss_seg3_bank .byte __bss_seg3_bank
pha pha
plb plb
rep #0x20
ldx #0 ldx #0
.Lbss_loop3: .Lbss_loop3:
cpx #__bss_seg3_size cpx #__bss_seg3_size
bcs .Lbss_done bcs .Lbss_done
sep #0x20
stz __bss_seg3_lo16, x stz __bss_seg3_lo16, x
rep #0x20
inx inx
bra .Lbss_loop3 bra .Lbss_loop3
.Lbss_done: .Lbss_done:
rep #0x20 ; back to M=16 after the M=8 store loop
plb ; restore caller's DBR plb ; restore caller's DBR
; Run static constructors. The linker emits ; Run static constructors. The linker emits
@ -225,8 +217,12 @@ __start:
; nothing. After return, A holds the exit code. ; nothing. After return, A holds the exit code.
jsl main jsl main
; Halt via BRK $00. MAME / debuggers catch this as a clean ; main returned. Bare metal has no OS to return to, so spin.
; program termination. ; (Previously a BRK $00 here, but headless MAME mis-vectors BRK to
.byte 0x00, 0x00 ; $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 .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 language-card RAM enable (GS/OS configures memory).
; - No stack base reset (GS/OS allocated and set our SP). ; - No stack base reset (GS/OS allocated and set our SP).
; - Honors GS/OS's DBR=our-bank, DP=allocated-page setup. ; - Honors GS/OS's DBR=our-bank, DP=allocated-page setup.
; - On main() return, calls GS/OS QUIT(pcount=2) to chain to a ; - On main() return, calls GS/OS QUIT(pcount=0) to return control to
; known next application (default: /SYSTEM/START.ORIG which ; the launching program (Finder / shell / boot chain) — the standard
; test setups must save off the original boot launcher to). ; GS/OS application exit.
; ;
; Entry from the System Loader (per Apple IIgs Toolbox Reference): ; Entry from the System Loader (per Apple IIgs Toolbox Reference):
; E=0 (native), M=0 (16-bit accumulator), X=0 (16-bit index) ; 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. ; wrong bank without this. PHK + PLB copies PBR into DBR.
phk phk
plb 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 ; 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- ; `[dp],y`-style accesses; GS/OS hands us a Memory-Manager-
@ -69,44 +57,28 @@ __start:
; LDAi16imm_bank expansion) ; LDAi16imm_bank expansion)
rep #0x20 rep #0x20
; --- Diagnostic markers between crt0 phases. Bank-explicit ; BSS zero-init. With DBR=our bank, `stz abs,X` writes to
; `sta long` to $00:007E so they're visible from a host probe ; ourBank:X — correct as long as __bss_start/__bss_end fit in the
; regardless of DBR/PBR. ; segment's bank. M held at 8 across the loop (X stays 16-bit) so
sep #0x20 ; we don't flip SEP/REP per byte.
.byte 0xa9, 0x7e ; lda #$7E rep #0x30 ; M=16, X=16
.byte 0x8f, 0x7e, 0x00, 0x00 ; sta long $00:007E (post-DP) sep #0x20 ; M=8 for the byte stores; X remains 16-bit
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
ldx #__bss_start ldx #__bss_start
.Lbss_loop: .Lbss_loop:
cpx #__bss_end cpx #__bss_end
bcs .Lbss_done bcs .Lbss_done
sep #0x20 stz 0x0000, x ; 1-byte store (M=8)
stz 0x0000, x
rep #0x20
inx inx
bra .Lbss_loop bra .Lbss_loop
.Lbss_done: .Lbss_done:
sep #0x20 rep #0x20 ; restore M=16
.byte 0xa9, 0x7d ; lda #$7D
.byte 0x8f, 0x7d, 0x00, 0x00 ; sta long $00:007D (post-BSS)
rep #0x20
; Walk .init_array (C++ ctors). ; Walk .init_array (C++ ctors). The `jsl __jsl_indir` and the
; ; `jsl main` below are relocated by the GS/OS Loader: omfEmit emits
; ⚠ KNOWN BROKEN under real GS/OS Loader for non-zero-bank ; a cRELOC (0xF5) IMM24 site for each intra-segment JSL, so the
; placement: `jsl __jsl_indir` bakes a bank-0 operand at link ; Loader patches the full 24-bit operand (bank included) to the
; time. When the Loader places us at bank $1f or similar, the ; placed address. Non-zero-bank placement works as long as the OMF
; JSL targets bank 0 (= GS/OS code) instead of our actual bank ; is built with --relocs (demos/build.sh / buildGno.sh do).
; — 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.
rep #0x30 rep #0x30
ldx #__init_array_start ldx #__init_array_start
.Linit_loop: .Linit_loop:
@ -127,86 +99,31 @@ __start:
bra .Linit_loop bra .Linit_loop
.Linit_done: .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. ; Call main. Standard W65816 C ABI: arg0 in A; we pass none.
rep #0x30 rep #0x30
jsl main jsl main
; ---- QUIT (pcount=2) chain to gChainPath --------------------- ; ---- QUIT (pcount=0): return to the launching program ----------
; Parm block layout in DP $80..$87: ; The standard GS/OS application exit. A pcount=0 parm block (just
; $80,$81 pcount = 2 ; a zero pcount word) tells QUIT to return control to whoever
; $82..$85 pathname long ptr (lo, mid, bank, pad) ; launched us (Finder, a shell, the boot chain) with default flags
; $86,$87 flags = 0 ; (non-restartable) — no hardcoded chain path needed. A program
; ; that wants to chain elsewhere can provide its own _exit/crt0.
; 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.
rep #0x30 rep #0x30
stz 0x80 ; pcount = 0 (parm block at DP $0080, bank 0)
; 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).
tdc tdc
clc clc
adc #0x80 adc #0x80
pha pha ; parm-block ptr: low 16 (offset)
pea 0 pea 0 ; parm-block ptr: high 16 (bank 0)
ldx #0x2029 ; QUIT class-1 call number ldx #0x2029 ; QUIT class-1 call number
jsl 0xe100a8 ; GS/OS dispatcher 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
pla pla
.byte 0x00, 0x00 .Lhang:
bra .Lhang
.size __start, . - __start .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; u16 refNum;
unsigned long position; unsigned long position;
} __GsosMarkRecGS; } __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 // 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 // drag iigsGsos.o into the link. fopen guards GSOS path on a NULL
// check (see __gsosAvailable below). // check (see __gsosAvailable below).
@ -62,6 +70,7 @@ extern u16 gsosGetEOF (__GsosEOFRecGS *p) __attribute__((weak));
extern u16 gsosSetEOF (__GsosEOFRecGS *p) __attribute__((weak)); extern u16 gsosSetEOF (__GsosEOFRecGS *p) __attribute__((weak));
extern u16 gsosSetMark(__GsosMarkRecGS *p) __attribute__((weak)); extern u16 gsosSetMark(__GsosMarkRecGS *p) __attribute__((weak));
extern u16 gsosGetMark(__GsosMarkRecGS *p) __attribute__((weak)); extern u16 gsosGetMark(__GsosMarkRecGS *p) __attribute__((weak));
extern u16 gsosCreate (__GsosCreateParm *p) __attribute__((weak));
static int __gsosAvailable(void) { static int __gsosAvailable(void) {
// gsosOpen is the entry point — if iigsGsos.o is linked, all the // gsosOpen is the entry point — if iigsGsos.o is linked, all the
@ -219,8 +228,16 @@ int atoi(const char *s) {
// glue, or a console emulator) override this with a strong // glue, or a console emulator) override this with a strong
// definition. Marked `weak` so users can replace it. // definition. Marked `weak` so users can replace it.
__attribute__((weak)) __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) { int putchar(int c) {
*(volatile char *)0xE2 = (char)c; if (__putByte)
__putByte((char)c);
else
*(volatile char *)0xE2 = (char)c;
return c; return c;
} }
@ -241,7 +258,14 @@ int puts(const char *s) {
// //
// Callers wanting non-blocking input or Event Manager integration // Callers wanting non-blocking input or Event Manager integration
// should call ReadCh/GetNextEvent directly via iigs/toolbox.h. // 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) { int getchar(void) {
if (__getByte)
return __getByte();
volatile unsigned char *kbd = (volatile unsigned char *)0xC000; volatile unsigned char *kbd = (volatile unsigned char *)0xC000;
volatile unsigned char *strb = (volatile unsigned char *)0xC010; volatile unsigned char *strb = (volatile unsigned char *)0xC010;
while (((*kbd) & 0x80) == 0) { while (((*kbd) & 0x80) == 0) {
@ -926,6 +950,22 @@ clock_t clock(void) {
#define FILE_KIND_MEM 3 #define FILE_KIND_MEM 3
#define FILE_KIND_GSOS 4 #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 { typedef struct __sFILE {
u8 kind; u8 kind;
u8 writable; u8 writable;
@ -1006,7 +1046,7 @@ int mfsUnregister(const char *path) {
int fputc(int c, FILE *stream) { int fputc(int c, FILE *stream) {
if (!stream) return -1; if (!stream) return -1;
if (stream->kind == FILE_KIND_STDOUT || stream->kind == FILE_KIND_STDERR) 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->kind == FILE_KIND_MEM) {
if (!stream->writable) { stream->err = 1; return -1; } if (!stream->writable) { stream->err = 1; return -1; }
if (stream->pos >= stream->cap) { 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) { int fputs(const char *s, FILE *stream) {
if (!stream || !s) return -1; if (!stream || !s) return -1;
if (stream->kind == FILE_KIND_STDOUT || stream->kind == FILE_KIND_STDERR) { 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; return 0;
} }
if (stream->kind == FILE_KIND_MEM || stream->kind == FILE_KIND_GSOS) { 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) { int vfprintf(FILE *stream, const char *fmt, va_list ap) {
if (!stream) return -1; 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); 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) { if (stream->kind == FILE_KIND_GSOS) {
// Format into a stack buffer, then push to GS/OS via fwrite. // Format into a stack buffer, then push to GS/OS via fwrite.
// 256 bytes covers most format-string outputs; longer strings // 256 bytes covers most format-string outputs; longer strings
@ -1269,6 +1322,16 @@ FILE *fopen(const char *path, const char *mode) {
// → NULL. // → NULL.
if (!__gsosAvailable()) return (FILE *)0; if (!__gsosAvailable()) return (FILE *)0;
if (__buildGSString(path) < 0) 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 // pCount=3 covers refNum + pathname + requestAccess. GS/OS 6.0.2
// Open ($2010) requires requestAccess to be non-zero for any actual // Open ($2010) requires requestAccess to be non-zero for any actual
// open; passing only pCount=2 (refNum + pathname) leaves the access // 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) { if (stream->kind == FILE_KIND_STDOUT || stream->kind == FILE_KIND_STDERR) {
size_t items = 0; size_t items = 0;
while (items < nmemb) { 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++; items++;
} }
return 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. // 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) { static int format(const char *fmt, va_list ap) {
while (*fmt) { while (*fmt) {
char c = *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) { if (gRecordSites) {
uint32_t targetBank = target & 0xFF0000; uint32_t targetBank = target & 0xFF0000;
uint32_t baseBank = gTextBaseForSites & 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; Imm24Site s;
s.patchOff = patchAddr - gTextBaseForSites; s.patchOff = patchAddr - gTextBaseForSites;
s.offsetRef = target - gTextBaseForSites; s.offsetRef = target - gTextBaseForSites;
@ -386,7 +393,9 @@ static void applyReloc(std::vector<uint8_t> &buf, uint32_t off,
if (gRecordSites) { if (gRecordSites) {
uint32_t targetBank = target & 0xFF0000; uint32_t targetBank = target & 0xFF0000;
uint32_t baseBank = gTextBaseForSites & 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; Imm24Site s;
s.patchOff = patchAddr - gTextBaseForSites; s.patchOff = patchAddr - gTextBaseForSites;
s.offsetRef = target - 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. // and shouldn't be relocated by the Loader.
uint32_t targetBank = target & 0xFF0000; uint32_t targetBank = target & 0xFF0000;
uint32_t baseBank = gTextBaseForSites & 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; Imm24Site s;
s.patchOff = patchAddr - gTextBaseForSites; s.patchOff = patchAddr - gTextBaseForSites;
s.offsetRef = target - gTextBaseForSites; s.offsetRef = target - gTextBaseForSites;
@ -464,6 +475,15 @@ struct Linker {
uint32_t segmentCap = 0; uint32_t segmentCap = 0;
uint32_t segmentBankBase = 0x040000; uint32_t segmentBankBase = 0x040000;
std::string manifestPath; 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). // Per-section identity: (object index, section index within obj).
using SecID = std::pair<size_t, uint32_t>; 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" "usage: %s -o <output> [--text-base ADDR] [--rodata-base ADDR]\n"
" [--bss-base ADDR] [--map FILE] [--debug-out FILE]\n" " [--bss-base ADDR] [--map FILE] [--debug-out FILE]\n"
" [--reloc-out FILE] [--no-gc-sections]\n" " [--reloc-out FILE] [--no-gc-sections]\n"
" [--filetype N] [--aux N]\n"
" <input.o> ...\n" " <input.o> ...\n"
"\n" "\n"
" --reloc-out FILE write IMM24 relocation site list (binary:\n" " --reloc-out FILE write IMM24 relocation site list (binary:\n"
" <count:u32><patchOff:u32 offsetRef:u32>...)\n" " <count:u32><patchOff:u32 offsetRef:u32>...)\n"
" consumed by omfEmit --relocs to emit cRELOC\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); argv0);
std::exit(2); std::exit(2);
} }
@ -1497,6 +1525,16 @@ int main(int argc, char **argv) {
} else if (a == "--manifest") { } else if (a == "--manifest") {
if (++i >= argc) usage(argv[0]); if (++i >= argc) usage(argv[0]);
linker.manifestPath = argv[i++]; 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") { } else if (a == "-h" || a == "--help") {
usage(argv[0]); usage(argv[0]);
} else if (!a.empty() && a[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"); if (!f) die("cannot open '" + outPath + "' for writing");
f.write(reinterpret_cast<const char *>(image.data()), image.size()); 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 (!mapPath.empty()) linker.writeMap(mapPath);
if (!debugOutPath.empty()) linker.writeDebugSidecar(debugOutPath); if (!debugOutPath.empty()) linker.writeDebugSidecar(debugOutPath);
if (!relocOutPath.empty()) { if (!relocOutPath.empty()) {

View file

@ -18,6 +18,7 @@
#include "TargetInfo/W65816TargetInfo.h" #include "TargetInfo/W65816TargetInfo.h"
#include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineInstr.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/MC/MCContext.h" #include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h" #include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.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 // first; the Loader's cRELOC `BitShift=16` mechanism doesn't track
// the placed bank (segPlacedBase appears 16-bit-only in the formula // 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` // 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 // so this is a size win too.
// ignored — only the SDNode's value-type matters, which is i16 //
// bank|pad. // 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; MCInst Lda;
Lda.setOpcode(W65816::LDA_DP); if (WeakUndef) {
Lda.addOperand(MCOperand::createImm(0xBE)); Lda.setOpcode(W65816::LDA_Imm16);
Lda.addOperand(MCOperand::createImm(0));
} else {
Lda.setOpcode(W65816::LDA_DP);
Lda.addOperand(MCOperand::createImm(0xBE));
}
EmitToStreamer(*OutStreamer, Lda); EmitToStreamer(*OutStreamer, Lda);
return; return;
} }

View file

@ -884,6 +884,14 @@ bool W65816StackSlotCleanup::runOnMachineFunction(MachineFunction &MF) {
// real test and a flag-using branch. // real test and a flag-using branch.
return Opc == W65816::LDAi16imm || return Opc == W65816::LDAi16imm ||
Opc == W65816::LDAi8imm || 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::LDXi16imm ||
Opc == W65816::LDA_StackRel || Opc == W65816::LDA_StackRel ||
Opc == W65816::LDA_StackRelIndY || Opc == W65816::LDA_StackRelIndY ||