From 42610e844ff0a2c337d873ef43ab2c36c7bc56b7 Mon Sep 17 00:00:00 2001 From: rajames Date: Tue, 6 Oct 2026 10:33:15 -0400 Subject: [PATCH] feat(v4.0.0): the nucleus is a capsule, sent to a node born empty MESH.md step 2. A capsule of F18 code is the words a neighbour writes to a node's port: for each stretch of memory, "@p a! @p push", the address and count, "@p !+ unext" and the words; then a jump to the start. A node born empty executes that from its port, so it needs nothing in it beforehand. - capsule.h/.c: v4_capsule_write, any node's memory as such a capsule - mkimage writes the nucleus so, to capsules/v4/nucleus-64.f18, and the addresses a host needs as a C file; the memory image is no longer linked into either product - mkcapsule is unchanged: the nucleus capsule is a built file kept under capsules/, as BLOCK_MAP.md is, and is baked, hashed and signed with the rest - boot: the node is born empty (v4_image_born); the nucleus capsule is found, its hash and signature checked, and given to the node a word at a time as it reads its port; PARITY:V4_NUCLEUS carries its name and hash Verified: test_fabric.c (59 checks, both widths, and under ASan and UBSan): a memory with a programme and scattered words arrives word for word in an empty node and runs. The nucleus capsule rebuilds byte for byte. hosted-check passes on three ISAs; clean qemu with STARFORTH_V4=1 on amd64, aarch64 and riscv64 takes the nucleus in, passes POST (550 of 550) and answers lines typed at each prompt (logs/20261006-102421, -102706, -103048). Co-Authored-By: Claude Opus 5.5 --- capsules/BLOCK_MAP.md | 2 +- capsules/v4/nucleus-64.f18 | Bin 0 -> 48536 bytes docs/v4.0.0/MESH.md | 37 +++- kernel/Makefile | 5 +- .../amd64/qemu-amd64-20261006-102421.log | 130 +++++++++++++ .../aarch64/qemu-aarch64-20261006-102706.log | 104 +++++++++++ .../riscv64/qemu-riscv64-20261006-103048.log | 174 ++++++++++++++++++ v4/Makefile | 15 +- v4/README.md | 11 +- v4/include/v4/boot.h | 6 +- v4/include/v4/capsule.h | 36 ++++ v4/include/v4/image.h | 46 +++-- v4/src/capsule.c | 55 ++++++ v4/src/image.c | 14 +- v4/system/boot.c | 86 +++++++-- v4/tests/test_fabric.c | 44 +++++ v4/tools/mkimage.c | 41 +++-- 17 files changed, 718 insertions(+), 88 deletions(-) create mode 100644 capsules/v4/nucleus-64.f18 create mode 100644 logs/20261006-102421/amd64/qemu-amd64-20261006-102421.log create mode 100644 logs/20261006-102706/aarch64/qemu-aarch64-20261006-102706.log create mode 100644 logs/20261006-103048/riscv64/qemu-riscv64-20261006-103048.log create mode 100644 v4/include/v4/capsule.h create mode 100644 v4/src/capsule.c diff --git a/capsules/BLOCK_MAP.md b/capsules/BLOCK_MAP.md index ca302eef..4fe58223 100644 --- a/capsules/BLOCK_MAP.md +++ b/capsules/BLOCK_MAP.md @@ -1,5 +1,5 @@ # Capsule Block Manifest — Auto-generated - + diff --git a/capsules/v4/nucleus-64.f18 b/capsules/v4/nucleus-64.f18 new file mode 100644 index 0000000000000000000000000000000000000000..44bc82e55757b094bade293c78d28df79d43ccb8 GIT binary patch literal 48536 zcmb__3w%|@wf@e@Ny0NgK(N(Y53N?f1}W;5myjnUB_tsU52Zd3(4auJAt)l1P_b4- zkQS|?VpR0{03Q`C3TQyCTEz&ytuJnk7Oi5GAU@$gv)1?RbN0Y7dhdV#^2<6ivtF}i z&6?RWv-kGj*)hs9S6BPbT$lAy=rM@4+4BZjIMmDBWZ@9V9qcU?eTI5b$v6BJW>z;@Ja4f=%Fie$6@5nKRtXdw^Y0Y= z7zjAhTe8ajoVxfNu@C8~zn|GBWY0)`4k#5AA(aPM)q2NP`!f~kvpsKs{dtDhN%S~? z>r^LQ)c1XnQ`Gm46&Bxxe))3AKVS>Zl?>l+%2hdv7T{GKt$axz)USMp$Yp*czc(lq zOhu{$(5>HPsNbbdeJ*m+TMwVI(Xudb$)RA$x_*lxf~pAgGk>-g_#yfR@bNrFA9mV~ zdo1381Ln_MtRLmBS$ww01-j?ARKrf%AXV%7h3ueQET7G{TfS-eW6CTXAMz&8yTWFg zt6G7#9R3?ywp;vj{|GHe@GIZutc2Z4yg!KlS9^`yZT{57Lqr1c2~EF#)|RWb2M!oH z+8;AjPPA}Lu`RE%{}Qen@mniz`}?&LpDq}x1*@TlvDYq}6)XhY zi9`b+`F#ca`^m3C`FxH1d%XJfmL!s20ZIiAASI1wem8jaIX0_<{b#PU%JN(b^BucC zVl&OPPC-ByLpm+LY@GdB>KH^C#pG`QUWwP(Xn*?G^!>;}+B;fxYPn52V!i*indaJ} zzjD-#0SJ(wQGgbAmThm-)F7T z=32AtRq?a>Q#Abr^w|^VFMg(_ww^j(q!&(9yXS`+t`&%%0DcqjQTzNXMeRF((Yqq| zL&*CG(BD6No+aJIbkl1gyzZdC$7>Wjn*JtD`~~cig>wJ#;i5PBn0_zqIBc=jm-+M~ z=2IWGZ%T@`Z;zC%(jKP%Pgwm7|2zwMkssyIuFPls8jAOPY-wm`<2S}casNphJ5F4= zBGjABya9@SWs%JlYgPF%WU&31mc}u7rdaCjw}kcG2m4ab-AI#<@&5)Z=LFgl?Owhv zY}cJI)ZhRw#qk$S$1F2*HS8NJ^*f-+pq&@O|MF6{eQk^Uri;Z@^I`wM?(hfNuP8<5 zA*MVecg?bQLVm;gmL|dXGZmk9*oc0?^ruPeMSp2QyPgI9*(vbPS?a&^15-Z0){k~1 zfBIo%O8s10J|A{wIpvqEJoijnPQT|sQw_Q2rD**2n>5bR&huM`OFy9>{u}l2*J*!a z{>1MrcNr(uZ(wiYUkd$W-wx1rMEg^}HOqEM`?-G9JpYo{lyADwoeDV@pncu?(ypxU zGSrWFv?uj8_BhS*Ctvo%82UTyNITOXOx%?I$9gyH?H2MQ@{jY?e&PB@z^`d=j@5(w zq7%wwAT6+&3D*r!U5DcVon{zyB}zt*=yW0Iq)dIoc--F^%u%7 zK^)n@e(w17qs(v8{vJVHeWYo3rfG@?Q18tt;d!^|&+9FYX;*0v(r$iC&b&Yc>PP|Ng#J?vZo|2w?2ctfI)2A{|%7Z?fhf$9az{?Bf zZyGPDFXKZz{gv?ZGrQ&`>f`=SFC90==H(TJ?^{M95E=dDIwGR=C=K-0IOAQR_d&c5 z1eur8N9kD4u_zb~xGJSF$71wVG&?_*xRvr-%^=-G;Ew~OzbrUK@uotbDvbLaPa0te zbAK)IDhF~n4ssqud?SCZpfq12^B>kH4Ri#t621S}3;*%zwZz1Mo9!>^JsbU}I->L6 z*y3MGKk;@dKJn&88gI2Z#Pgcfp5)K=j23AFg@Ui4y99RmEnv~&6{0`sxlUv1ca4>k zmRlnC(W4})*c`nUfxaFvmW0o@v}@DCdo88mXF`nUm4%{{=Pd=FE8|f5!HmerB^Ezu z?_b$e{JIYKe+DdHJxu0BZhgv6yg*DreQ4J^Bbk!_9rSeXTiKpQeukA=G)nIeqfo)$ z`N&`F&q#h)vAdzqO28SBbkU3LxDI|8dw++tGxcTrReSXfmM`la85rjOF!-(oHM%B zpMl>;fNaOq@-W^PDBlaXHG=h405QgZoE`+dZI<8HY8_E!Oo%+Kc^!`lVNnk@c&BQDI4k zfq!Q}pk>Wwm0LPY43Zi+)nnNxy-uH z77PCg{1VK|3Q{!=a-E?lRqtiQG)Gx1&OJt$S-45Tjx z{-D%gy=kS@lYT(F`iPcu-C-x{LH;Hl3V-Uw{Byv6%`zR|8JDQHzdt-5W7k6HbBoeTPvPCoTyJs7v?$4t{-TK0wG^RME1truC&{3;y^u-*U1 zY4^*VcE2J?yOWOXZq_;SST_rs!M736)ay)}|8QV;y`Q7K3C%oAIyA>$_RniU_aOA) z{Wa+r$LIHWPvRWyXXe54tX%5Lb~N*Okwbr?U(o(c-w(O&x=X;n=|ho6zCr&j)pl{^ z-j`bcpw)-p|n5h zLphPC_7{#HMi23~?N{pl4E04f_6nmq51>9Hp>HIr<1YEES*H8Pq&Mw%o7M9luKk>L zrC%7mWF9#J`uUlv59$4;x!$Q_Ch|5hn z{~&%PbFbbcaH}BM)x>P5djV$umG*dntDCgVEi6ElI&^n~1s(0pV_Oc7Xq z#H0a2!gOwa$qA zzvg_@+>9n8UbnNq1{_rIF67RVVjT0>%$79y#Nbsdy%sRjHXN#M7CChJL ztS5&GE6Qczzc9Rv=oO|Zmv7;J?mV08Vi&{jeuw+~8l>I+&GGV#G@es9PV}WcCxLDo z;;@k`{e3j;hy2H!d^4{QKGKPd$}_;HHTsmq8}{!Zu_-mSMoiE2r_V@CPs^?e_5T?c z+tT=b2>Wp|{l0^K7ePM91J?UObQNlH*}%3B4xn*b>% zWe5ZS+=24dfIO$L8F8HRF0SJ+9&SZ>41CN!xcKdG)GNTBrSlfzZ-l*$LBIJ7_`Gl8 ze2nuiV|S@9{UZhAJMFXydfyFb<}*1^NdWcW`f1AqT|eOXZ~Q{|cO0$zOr?b-*_M#^ z@#OUk_-~HOllpQVWBfbE;!cEq)Z=Ghz;q_khL6aN7ETPE?_|ARgPf+IUiCR370|wT z%ozKlu%KAlKRkMR-aDXs3y^mB2==Evxb928G3U!d>%L|mUgVS)>wJg${TqCn0o^!h z)=k7d`-1+VX)|BTv3i?$Cf7Fj0o%9zqbHA`%+JSM3`tUyfzoB2X zGtWxTuPC(qo#XLdZ!Dl5fZf>N(veSo?b6j=?Z)TI_*k5qr|n>wCS3hM_e;R<;U|7T zy;+aJu*b?sG&FuQezwE;kIJfCncq6Yv^NBD2RQOGkWQ%2A>|hZ@)@^v+%e~e%+j1( zLdSOExy!VIGO=$Z_Rr{-V<0yNusW*opcv`i@cW;D`Dy98?&B|=CHqDh$PeyQgY^&X zH}%dw%OtdWqILb;F9)#|w2Z$Ld30kU1GFYjMyzhRDjT|36h z8yjtt9Ay81zCOp6b6qHHoX(#$I>+@}3%ynWvR?EH(lh^Z%m>eO{FD8Jewmg#D%Z+) z?XdxJ{`v#uorC&NKI2TnupnxA{fIPod;eH)N= zvURi<;|=3pq-><*)BazAt`*R&2kBP8{=0$a+TZQ3)W0|Uf%xo4KQ zKnhC>>`zoLXxHwjz_&^2M|#%V?cYH@&X@kZt@w)dJ>oG=w^d)(hxRt-VWmE%*(L6> zy|RIDI3V?--;tjAY!~X|whQAC%kwh0f1~S&jNjx(KOK^x@tXbH_w^jPpSk8$+YaV= zAQ|6T|N4?WPfLE&LK%mf-*{M{8Shsk|80Sp^PW(s&t=i?3Q#fn5$j3260gX?qrW9< zSJsF0#h|BM-2Tb@r{M?FU@x;CEAyCh;&zdG&~9!!P=AikET_B@AdoNlFpf|U;=A=2 z!+JRW_zfELO13l3HM0HDO}z1#7tr26){cRE+{Y*ADdRl-CQ=}`Awhp#47~>i^>56v z{*aG!d^}Zqv)`myBy**XP=@R;D?qo@;ph4j>rH=h;|=|zEj`F5T2dwzlflO5MSbpo z9@jhi6e7*}y;R2hqo0++uhNby5x!+$Oui3*{vL<#D2MN8q+PzQT&}wW>jrpE$z3$1D%xGmbn-dH>S$*7VC7 z&`si#}+?yvJaFrVY%b2RS$I``lF-NW+8+d}TYnRPYXC+Rs~=I=(mpUlwv zG}eo0)}updnT!jC*}9HWp^ve69LHMFz7MnA@!SjLWc10;wfa=$7xlG}bYBAh3x|&F zlG$g1_+3Row#vsjkHP|B6Y$Z~37+?z!-wbDy2km8wDQX;MhV}lDm_rA+~Rt*gM#UR zCT`?dI-Yau*r!m|O^U0si)?;b+2~NcvOw3>p{Jil$BrwubnbocPl10p>2~YBOAF%u zh74`Tb=Wt0GNV4n^5K=1tLG!bPgQu{G2nL;;N0dgU+(K{$oRvXmKXW5{kguJJ9TQU zEoXY#?Afuzbj_T)aQd8B2!HadbEjE+SAY5)`Bv6Vo|4G-{8-I}iRo#z=Y-a|Yc8^- z{~Nw@LhIvG4P(oPddvXhw)Cx-RyQY+u69;zc4FF8=lsO9sgBrza!hq<63bDYHtqDI z<<6NkHBruwwIkyu`}KwGbYF_`g6(*HyKvmS-AS);(tmT(Tb%UsPWn|R{Vyl|rIXHR zAJ!+!N&knF{*{v+?4*mF^aLmE+T%4(*B?r!ojW&SHO`UxRN= zxZh2V%?k0EdG<8vhspgIeCEtN+sdO~POFWH229t~#KQe<*0~n_d;Bz+ep9L~9r;eK z4fpptRkuyP1=FX5^qXEYD`el=i({ewJZFZe-zMME$umR!Gd6R2s6Wr1t`%>~p7}M} zf~;q4&5T%Ly=T|ehT3UL-JDRooMr@v@MlgA)mN?9w*CLlddxn^55jR|ns3!?)2@}( z(?ji1=j>n3nHBcq|ND5N{#RaG6SCX1DRrUtof^9+6n|@_c5TIc@n(Ztb}tznD2K>_;(Um_)v@nsY*OYRy2KST2<{8EwhUn^EJAyCFU5 zjG#omF(W83t)qL}a!PArp*Um2hR4V0;d#Z`)24^z#KPm{FY02mL-f)JKftb~2gTAJ@$fJK0nfM)|<0Lb%{<~&1= zZ6BWJaL=XH;r_+q*o*5GbCA#bed1lxUe{^d=M#uu-r8r2<->U+<(f?mxTw9&^}K$ZPcNOa&8Df(pTXZ> z8h)P(%Nw9K_lI8PU4tiV(cpsh<6{U?n?zxKFpjUNN z;}_}9f!r;aPtd+*(Mzh_u1{{BtzTJ1)gkS12js5={434!E!O@ zp8@&4y0_k$(`56_`_=?Tv$Z}|Hq%_B?+Chfq{sV8$S0s5Q83QZQD6JpUfn_074WAW zxW27+WdDc`4AWEo5uiI9(Co*EzoiW<5L0p;j`+QS7Yp!pzdNl!_noSWR4@KN7I?oT zex^QO&2<3ZS$B91{=;!{NUS(C9$z?l#&pY@VWDs(;TlP#GDfR~&|@~B=~ug~vXP46 zqBHUSfbz@Zc+V(aP6+QRlrJV;<`9V!DaC@OMl?UKLHRNl?>Aw*Yf-)e@Y|)cHU4B? z_^HKAD=CzC$M+Lb-y4B{17PdY8l}VhB7Iz`*rN=AfONM3zX8zq_dIXqq~zHD@%VNZ z%KwXaP5PXAO7AeBF8Br>a!bo9|!n@|0wkwczg(N56VA}<6UDBl8-0a zr8sU3i4}y#kqf8K4y9{mh3^|Lj?D|*kLlfY^83$?%XFXkFKB49|Ma5O?|P)WcGNg) z=KG=c%<9D68O6B-ThDU<@1-=f2Ey=g3iwgKc8UY^?EBWwQ_iYg}jD!)L;AHNBJEa zH`|H50NwU4rCv^ZkL}p_p=IFuK@YqSiuc8=-+1uJ!~0TM zuZe5*yv_~uH;jLzJ0pqw$v;y5xq*Bg|ErU<2lZ%=^62sUe%z4pO})>Dy@~;6chqq` zr(@$TYnS#N>z}dEU#jl|v%l?S*a@pIF&{|L@Sug}|3%>E11>m-ufA`~&-`5c^&tM> zME2pMKyw`WiMNeN%JwTu?g* zegJ�W|)8mn}-2s2jt?dl=>G;&=~Oyoj@Z{20m~CEi*8ep&DaVB&8R=6V90rDZFiaWX|1+R}67YqfABLZkA|Dc`mg;>b(>p-FEzYm2ElUZj?Y)ZfR|33x zu|rB&E$>Z~{{zs+`_=ym{kC<~=P1p4Fl8Jua#fyAcd3@w4ElEg8P82x@FRNcJGkHd z2za{!xr1{ovzYy7uGFexSvO$2wg7Lpi!Y<3!5^+CI*I=U@LC=GeTa)uyf3;`#RYnO z3;eGgIzE3K%?qDTq`cHl$|wcU_-BsI;ro?S%X6h`62AlR+Bx`KCrL90)h#`r!ysK( z;Ac8?JjdcU4VC#Ns_FIar1OiPbvpDs;iyiBo+liS{9}^P{nDZ1JfR=t4eX@z1oGiL zAv-7!&l3iaUy^ykPdd5t1gv`w`-L@AaGeCZ97g->)blA__bJN1*#1R&zsYz>2E#x% znDltwo(FV_I_bFNz7H)52BzI*J=VN$P3B{i*T20+hH}*3rB7{xo^hqxTlAzwjaC-R ze31SNa<2+{=K}r~@^U-r{kG1^?K}g|m4(gcoesKF0nPg;bI9AmUY&G4%Kpvuskgk5 za=)0Y++XZVIp34*Lod!-2S7+I{Q&dXlaX)gqjBUHxUob2Z0I>T4rTm$eaOSIesu}j z>!MEdH?4Q%7vTUlAM_Uin)lCbvLe`i^dA!k&C-c<+2zugk%w9&kxeUzL9i z^xA;={gO^mSubKcvwboO3iGYJ;xYOhq5W;I<>0#*kk2n-`WlZrMTHZULjT_Y{OcS# zF|I*jyh?P&Cu_IftB&(Za@Rp_S=1$GLz zU(3>)#s9f3_Gn<=@O{+aPGSEw?JM$5?WFTQ(>{{Ei+;CM*Yn7iwlncS@JR2{x%xZ{ zeR%)o(kGK2>CcsGmHYzid1U)T75Su&3oEh{ z^ly?+5T!MyT5z!I;%!PY!lpzpgaS2G0v zk48N^0`~8$^AC5t<2nV$%RyL2Ukf>uI|zC(uH1`qo;!8p7VSVje}Wx+jK}0lh1oA% zeypzv;j{`3I*yW}dLcvgCUh=R!W;Z^B-FDd_k<@rBTLKJYJvzAjw@#-ATrf1}?ZtIrJZ zYry_E>&<;9_BZ++{ndv*v7Kqx<=|`lR!~pwT<=>sbK$@AfA$B{f22HZVC4jhms@WC z$FJ+q5BmZJ^EK>C*1dg6lf~Y3ldjh_-&FIdO|xI74J;dP(|QWq{?hAK@Vk+G*XVb~ zOj`7g4h-*elI||x-x<)46nQsxE)YFZ2kM54bUH(HKk(L44)#UOeyGr;4%Ceo;y(<$ zhXCFESA(Bpbs_!+;5`c1dY|ssa(+uYnEP zi^+8wcikp3E^MzOz~?YP`n^euzNxu+p$ScI;2j0nhjN|uY_?D8nB0jljex$OI|lIG zj(?E*v;mpe$Gt}NH_yv$vIMDP_@u6K`8DXW9r{6}2b~W-=@axlEJa~9-eAz51n4(C zwA)Ic{olp9v!8k1Dx05PQdwkS@ks4w_P4$AKtCL?81|@y{ru8nW!&QQu_Cb7U}>kZ z(7WZxp`rV~F{J0ZG4?s!c~pB+?TS-$+3=YM@{y^!qI!@1F+ZyjiqB&zpsu(vZ#u{sn;ZpueFPKMwq{AiThP z3Gn9v`m=N$nEf@iAiR*f5O@oS-=yOv`&*V|z-KvN-SsGQDI#H`3#|7I=0D{(@zZ_jX#t>Qr~n@Vv!QAj$M!d%2ibp}Ot^usiVVQ+#c_re< zrK1ybJ@1#mKiZ*-0j+&@l`MGW7UTOFvZ@#7kqx@z0RNNig83jZb6D@icS-xRe#Xuc zw@&Gz&tvia(e#`3mX_LImhE`ZciVJd zCB0J5uT|vBbj(&!uGwgZk${&Zu`kz|s0a01m;^ueiMGc>Xpg@J?fB>(D|p*|T26j^ zALUZ;qa6aedR<9IFu7K?mt<>kLOQ|^E?Xde?K)>k{ozJ3tZ4&!F64Xom)t@y~;@rc?Zzvfn;~f2s zdaUhI-(XoA|HL@B`FWd;R*o*Vu-yJjxc&yc?nIl+X}wBsVCmY_c_vSz0a*fR~_$@ zeV3#O?XnW@QRDm6>hT_Ko@*qYsRzcJY>Dr**I|$|f3d#rnf^_BziG;EZM_YVjAPH5 zFY?&*QTCs?xQ=O>OZ>uYP6{5+H!@1nj%oOwgu9-1gp7Kg_ciQ8`pRjOYpoonr&Pyk z64SNj0rtf7keE#wy_V`ZKDJlKuBsQ;nSb4NiPVF3=-5@CJ7RiZ*I*o${=LqrSN@#Z zkX-Y)qAamf&zNXo?W20opQwl7CpAvv_fbRM>3OHX9x*_^mmX|AKA*<=898U#vMFd! z_r1Ll5RU1p?;Ag>tHzBrW%}JTw)Ylq4)_dec(L;OZK z)Ni}$e3o+8@%x9)dn!q1o(~iG^v6+X5AF}8jo0~_^Vu|SD(saPhw{sP&aVaEQ{!`d z7sGDbAJz9eQ;)i?;qNYRUC7v1<|EXPeEj|Uzqa<75%(YIcj<^{qnsBs&x4-R0h7IV zbOZD=@39uUu)P?c%M1P`a@j6d!`|+CO$V!-x!51K?$h>Xef`$qi)}f-d&mB89{Lac znQ>)x61&~#*zFeh8|_9t$>+~qw|-;glHYk<8yjt!en9+NU{}5uocq`RhTUv`v`HU)m?nLJUViT(EieGW+eJ@gV!`=_1#}Kgnl(qUE`A=Oxaqr1JkA z)Z_1f+}}F_I80A+(gU6JZ=7^;`uZG;%5f`dK1g8GW9+|#>oe%{9w6-)9|s$AEP7G) z$%8Fqd2B+s9cIQVL+vnGnp=a^@zHb0+A{gdBfo_&dZn*6X?XokR=I>oB&z*uSDo7r60x z2b6l=0`R{8uwlvYkAlL4AIIajiLam9pUuA;y3j(#+je97SJ`w~wr)&G!e-!F0{)i+ z)~(nfd{|C@;d5iDWBdQwmht}XI?(+I(5$n_I1#tBax!!S66tOR{>p>s6rcYy#_B<* z{M-^5GAMU7=x#lTo}LuQ4Q)8A0sh^9X8e%IXY`ZtnQ|XQ`2&DvKSl7;#`e#(IN{w& z&wB)T4+ENg9^%WmtxwyLyZNpX@HPM%qfrk;G{xP5^38x|zlM0Bjew_7{uH2D&nI4J zL3|s^p93`KC?xI?KZJhn||_Gh(Gps%zn)a zHs4=0NBRT#w%#}Nd7ICDnVmS#@=dp;yKVk=-J-f*YA0g-^$O*()}&=61V<`WobQnwSo8X z>wR@!EwlDc%lEB!_R73&F&t-p>uF6k|Bpzs9u4O0);@Pzt_sFMnme$kU zCyPC<1pj#X$V)AY>?|DzOR{tvs9vk*AU@x}gxzP9Atoj@&z&}%Ou@mWA@%%9Ln*g~!*7wIDeY(^BOp`bFGbbg%cm1mF zeci93{dWzYzRL3AIWYQ11D|h(AlA$93+>n(orUK#lK6+azi8t7@zmYInOU0Jg*d`< z{Hx$MrhmzL(8QayfAJjb68O)>sJDsR((f+GDwR0FwBK}r`2VHg_ZPIce}=BVn>U!s zlEUwu@|{b#-#S#*^C`}0c3y$b$xHQJr+VbX&v9`}F#LO;F<`_%9!)?)?n z5B)u=dVY_J>mPs%eu93;bFk!hb6^*pmzj3UPE-BY?_-uy3JC;2nl`H`SL+Rpyc zYh>RqenM3>VLbP7PUB0^?OL`@#)DA(Q`jUR*b?6U|D_`o7 zOh1kTq~DpfP2_%%rTdEiM7w{0{_%d6uERBFOL6mUVlRp|D}A=U62L;gX zei(DkAHm)Wc5SS=-zw8`t-c?Dbd1}iKdHOMQRbfvdyMF=eml6k#;XzCb$&6tyZSAk zJ6nx?Y4SJzGSSL4P{tM44s+3ugZf|5x5DDnPewuh(U4bvt$ueofxq}8{nErg*{>=K zjhdb}8h+CrumXPb7WmRX-h_Ync<;B>#a=g|eNOFOzuBfw2c0{vO=CPoeUr6E1KNXp z{ZY$5w|vqRy_cM;ypg#1;2gO>HFwdK_O7SaXJ4Mn810r_(m>Ha+FrO^Dl z5#_tOrAzxWKlVQ3lID;P%NeiF!uUnLu~Dio^ymp`d8 zG_qcgezAbp z=rc|`(C_HKPj_D;?QmfdyHg+f{Y#X)G;G(}2en4Lzck-PmHxv1_A2aQ?pJdxCh-}! z-sqkw_j~Sn8m5(dJkD{x#(YAKN9MVmEjI5~_e0W)4|}hU87E}{+Fg$tU^|^z7+Kct@ zS1;eAoWuXkeW=X0W6c_0=qKuZo<}!>v?ef7D>I$da&HlRX7Xg z=b&C?fQ&2mz>jNCPa{n1Ja>_fM|Y$AE z>Vk}BybsRJ_s>v0wH?g+jUb2P8|`{3^7))6pQEMSXs2gk_YGX ze=U`HM~?l?_(6QiCqC20-XZ+h6&iotcCkF|<>II3jT>q6M>-p+U&1f%25dro@9n{L zv0OQSWzMsgS$cgtsr`lPFooYa^z9GR_Hy~GS*GtRqkpiTru`+}asQP0^lS5ctLTwF zuDrlXjGu0nye`7DXFYQ#BE9c4!v-8GT-Xmc! zw~_w?a(V)8?a`P+T`b(%qh8W%zqFAh!cU(@i(~cy{n2qK>o%PW$H@3mb!v8=#U5uA zv9Q8Xq(A7g0Z)8+yWB~$zLqBO@>|M7{hDRb65ZggVg}$a@HOWVKlvC^9Kii-@ul7-b>E=9@Zfab6;Pv%zhT+`b%GxdNR%OWaAX$ zKI1#j=?nqkh9vTWb96X2$$GKgTMyT`Lp|mfOc6gNop}yN;*9&e{;b2e{;luTi9wIL z!!XY+)p z7lFPapr>AH4~}nF0gv|bGvASRt4F+Lzcb&>%eDGR=e9p^ErH+V0{$6xSOYs5DC5HX zm&0=TUbEJ{W6!j#_`WjYQ9kuF@mK6axuoYrY5pSBn|v;ZJ_}Kg(P@oyEp5ZMdM=y% ziNA5#=h6?zm*=-B-#!1raW*wOd${GxcDNsMmjg0hlFtU(W6{qfUXaf6uvZ|zNuM`& z<=Y8sAf4 zDqv>M`W(&C|DDK(@g!U8>#*_81pVm_AI6n{593M_KGmQ<$Kk{H67XSsNy6s>(APM8 z7-u@OUO}8Ww0-7*ey+oZ{WiUi-WQDed0#P@7~lN54EPHH-S4-QAAQU#mVoxzf^$di zIit*;`W_s9C;6hGBF6i<0`I;Qj3@Z6I?r9++!OcJ$S2+L_+E20zH9zR=*RE5xZkhg z_)7Xr^yeT>2L7cQu|BtfFZo~N_*D?+=~t)_>F)vkU4W!#c|cEpa_IjGx(6Kk2<#Tn z(~lhb$3VBpP}kniX4v0V>-2e(?-1y7 zfWY#0x%sl6m|djbVV1=hgTwLn73lK}prI2MJa^rm^mq|J(|T#@TSx8=*s-f4hkV(-84iE) z?gqXc0og9q$^4~d#?6-Yp!V)fjtY+ezn%_X>U&I}udZ8dJ^VOX=Q;p3YYx}@LBFLM zexNmS{lF3H^@F_LN#vgB$UV82?(+}t<<7&7+oGzd+*1NNoqy3^hW85ZuWW_hgbjyj zJ&14CPu5z#2m4vLCi&2RK$3cl3G`9>mG{#5T>XR{V#jyk059LI>6li=^sCJe*`I;G z6_*y-bYVqtm4&=2A-C9(&vShA6W8xOG$8TZa<(tq^%m$k3-#I2v$4_YFdgahd+GeL z1@$@#3i5d}kRO;9ZwFbA>P~+*4-Q+Bs_b%^dK8b*dKQn@`wX!2YEaKp0Xcs0ITpho z_g{Ly%;#8sAGE`3Pg`CA9-ez-T%Ygg7xbSZeGfrZ4mDAD8T7cwu|MMmv4eKx^QYOx z1uV003Fxmph!57G-`OE|O8lNX#~Hs#zwbo-6L}{7=D6sC5AWZJLq7ZtJn{HE5&09( zt5?cDL0{tY`63se?L$a>KBwgB;l(tEc%P5ZwC+0FaYyUjfPB(ZAL_?^Lhb`?$ $@" diff --git a/logs/20261006-102421/amd64/qemu-amd64-20261006-102421.log b/logs/20261006-102421/amd64/qemu-amd64-20261006-102421.log new file mode 100644 index 00000000..9dd72b02 --- /dev/null +++ b/logs/20261006-102421/amd64/qemu-amd64-20261006-102421.log @@ -0,0 +1,130 @@ +[=3hBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0) +BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0) +[=3hStarKernel UEFI Loader +Loading kernel from ESP... +[CKPT 001] Entered efi_main - ConOut live +RAW SERIAL UP +[CKPT 002] Serial (COM1) initialized +Monolithic build - kernel linked directly +Collecting boot information... +CmdLine: parsed OK +[CKPT 004] Command line parsed +[CKPT 005] Kernel stack allocation decided +[CKPT 006] Boot info collected (ACPI table located) +GOP: linear framebuffer found +[CKPT 007] GOP: linear framebuffer found +[CKPT 008] About to enter ExitBootServices retry loop +EBS... +EBS OK +Calling kernel_main (monolithic)... + + + _____ _ _ __ _ + / ____| | | |/ / | | + | (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| | + \___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ | + ____) | || (_| | | | . \ __/ | | | | | __/ | + |_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_| + +LithosAnanke v2.1.0 +Architecture: amd64 +Build: Oct 6 2026 10:23:55 + +UEFI BootServices: EXITED + +=== StarKernel Boot Information === +Memory map entries: 131 +Total memory: 1023 MB +Usable memory: 968 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 249253 + Free pages : 247730 + Used pages : 1523 + Total MB : 973 + Free MB : 967 + Used MB : 5 + +VMM initialized (mapped RAM, CR3 switched) +VMM initialized (mapped RAM, CR3 switched) +VMM self-test: mapped OK at 0xffff800000000000 +VMM self-test complete. + +IDT installed. + +APIC: init... +APIC: IA32_APIC_BASE MSR=0x00000000fee00900 +APIC: stale-ISR drain: 0 EOI(s) issued +APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear) +APIC: init done + +I/O APIC: init... +I/O APIC: base=0xfec00000, gsi_base=0, overrides=5 + override: source=00 gsi=2 flags=0x0000 + override: source=05 gsi=5 flags=0x000d + override: source=09 gsi=9 flags=0x000d + override: source=0a gsi=10 flags=0x000d + override: source=0b gsi=11 flags=0x000d +i8042: keyboard ACKed enable-scanning +i8042: IRQ1 enabled in controller config byte +I/O APIC: keyboard IRQ1 routed (masked) + +Timer: init... +Timer: init start +Timer: PM_TMR_BLK discovered from FADT at port 1544 +Timer: VM mode detected (hypervisor present). +Timer: HPET calibration disabled (VM-exit MMIO would poison timing). +Timer: WARNING: invariant TSC not present under hypervisor. +Timer: continuing in RELATIVE mode (no determinism guarantees). +Timer: RDTSCP not present; using RDTSC (less serialized). +Timer: CPUID frequency unavailable; trying PM Timer... +Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2097406618 Hz +Timer: init done + +Kernel heap initialized. +Heap statistics: + Total bytes: 536870872 + Free bytes: 536870872 + Used bytes: 0 + Peak bytes: 0 + Heap base addr: 24641536 + Heap end addr: 561512448 + +Heartbeat: init... +APIC Timer: calibrating... +APIC Timer: apic_hz=1009199508, tick_hz=100, initial_count=10091995 +APIC Timer: configured (masked, ready to start) +Heartbeat: init done +Kernel initialization complete. +Boot successful! + +StarForth v4: one host node, the F18-derived engine +V4: capsule v4:nucleus-64.f18 signature: missing (unsigned) +PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0xbcc90cc5a638a299 capsule_hash=0xbcc90cc5a638a299 words=298 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x88fb8dcbb051f7b9 +V4: capsule v4:post79.4th signature: missing (unsigned) +PARITY:V4_POST tests=550 pass=550 fail=0 +PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb987dbb4388990bd capsule_hash=0xb987dbb4388990bd dict_hash=0x843716689a31b60a +PARITY:OK +POST: PASSED +ok> : SQ DUP * ; + ok +ok> 7 SQ . 3 4 U* . . +49 0 12 ok +ok> COLD +FORTH-79 Cold Start +System initialized. + ok +ok> 3 4 U* . . +0 12 ok +ok> FORGET U* +Protected word + ERROR +ok> 9 KERNEL-WORD ASK9 ASK9 +Argument out of range + ERROR +ok> BYE +BYE: cold restart diff --git a/logs/20261006-102706/aarch64/qemu-aarch64-20261006-102706.log b/logs/20261006-102706/aarch64/qemu-aarch64-20261006-102706.log new file mode 100644 index 00000000..c453b0fb --- /dev/null +++ b/logs/20261006-102706/aarch64/qemu-aarch64-20261006-102706.log @@ -0,0 +1,104 @@ +UEFI firmware (version 2025.11-3ubuntu7.3 built at 15:40:26 on Sep 23 2026) +[=3hBdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x2,0x0): Not Found +BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:04.0-1" from PciRoot(0x0)/Pci(0x4,0x0)/USB(0x0,0x0): Not Found +BdsDxe: loading Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0) +BdsDxe: starting Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0) +[=3hStarKernel UEFI Loader +Loading kernel from ESP... +[CKPT 001] Entered efi_main - ConOut live +Collecting boot information... +[CKPT 004] Command line parsed +[CKPT 005] Kernel stack allocation decided +[CKPT 006] Boot info collected (ACPI table located) +[CKPT 007] GOP: linear framebuffer found +[CKPT 008] About to enter ExitBootServices retry loop + + + _____ _ _ __ _ + / ____| | | |/ / | | + | (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| | + \___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ | + ____) | || (_| | | | . \ __/ | | | | | __/ | + |_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_| + +LithosAnanke v2.1.0 +Architecture: aarch64 +Build: Oct 6 2026 10:26:41 + +UEFI BootServices: EXITED + +=== StarKernel Boot Information === +Memory map entries: 108 +Total memory: 4093 MB +Usable memory: 4056 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 1039869 + Free pages : 1038458 + Used pages : 1411 + Total MB : 4061 + Free MB : 4056 + Used MB : 5 + +VMM initialized (mapped RAM, CR3 switched) +VMM initialized (mapped RAM, CR3 switched) +VMM self-test: mapped OK at 0xffff800000000000 +VMM self-test complete. + +AArch64: running at EL1 +IDT installed. + +APIC: init... +PSCI: no DTB -- using HVC (QEMU virt-machine default) +GICv2: no DTB GIC node -- using QEMU virt-machine defaults +GICv2: distributor+CPU interface enabled, PPI 30 +APIC: init done + +Timer: init... +Timer: AArch64 generic timer initialised. +Timer: init done + +Kernel heap initialized. +Heap statistics: + Total bytes: 2147483608 + Free bytes: 2147483608 + Used bytes: 0 + Peak bytes: 0 + Heap base addr: 1207959552 + Heap end addr: 3355443200 + +Heartbeat: init... +Heartbeat: init done +Kernel initialization complete. +Boot successful! + +StarForth v4: one host node, the F18-derived engine +V4: capsule v4:nucleus-64.f18 signature: missing (unsigned) +PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0xbcc90cc5a638a299 capsule_hash=0xbcc90cc5a638a299 words=298 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x88fb8dcbb051f7b9 +V4: capsule v4:post79.4th signature: missing (unsigned) +PARITY:V4_POST tests=550 pass=550 fail=0 +PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb987dbb4388990bd capsule_hash=0xb987dbb4388990bd dict_hash=0x843716689a31b60a +PARITY:OK +POST: PASSED +ok> : SQ DUP * ; + ok +ok> 7 SQ . 3 4 U* . . +49 0 12 ok +ok> COLD +FORTH-79 Cold Start +System initialized. + ok +ok> 3 4 U* . . +0 12 ok +ok> FORGET U* +Protected word + ERROR +ok> 9 KERNEL-WORD ASK9 ASK9 +Argument out of range + ERROR +ok> BYE +BYE: cold restart diff --git a/logs/20261006-103048/riscv64/qemu-riscv64-20261006-103048.log b/logs/20261006-103048/riscv64/qemu-riscv64-20261006-103048.log new file mode 100644 index 00000000..9e9b7da3 --- /dev/null +++ b/logs/20261006-103048/riscv64/qemu-riscv64-20261006-103048.log @@ -0,0 +1,174 @@ + +OpenSBI v1.8 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : medeleg +Platform HART Count : 1 +Platform HART Protection : pmp +Platform IPI Device : aclint-mswi +Platform Timer Device : aclint-mtimer @ 10000000Hz +Platform Console Device : uart8250 +Platform HSM Device : --- +Platform PMU Device : --- +Platform Reboot Device : syscon-reboot +Platform Shutdown Device : syscon-poweroff +Platform Suspend Device : --- +Platform CPPC Device : --- +Firmware Base : 0x80000000 +Firmware Size : 321 KB +Firmware RW Offset : 0x40000 +Firmware RW Size : 65 KB +Firmware Heap Offset : 0x47000 +Firmware Heap Size : 37 KB (total), 0 KB (reserved), 12 KB (used), 23 KB (free) +Firmware Scratch Size : 4096 B (total), 1464 B (used), 2632 B (free) +Runtime SBI Version : 3.0 +Standard SBI Extensions : ipi,pmu,srst,sse,hsm,rfnc,fwft,time,base,legacy,dbcn,dbtr +Experimental SBI Extensions : none + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080040000-0x000000008005ffff M: (F,R,W) S/U: () +Domain0 Region01 : 0x0000000080000000-0x000000008003ffff M: (F,R,X) S/U: () +Domain0 Region02 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W) +Domain0 Region03 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W) +Domain0 Region04 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: () +Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W) +Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W) +Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X) +Domain0 Next Address : 0x0000000020000000 +Domain0 Next Arg1 : 0x00000000bfe00000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes +Domain0 SysSuspend : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART Priv Version : v1.12 +Boot HART Base ISA : rv64imafdch +Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 2 bits +Boot HART PMP Address Bits : 54 +Boot HART MHPM Info : 16 (0x0007fff8) +Boot HART Debug Triggers : 2 triggers +Boot HART MIDELEG : 0x0000000000001666 +Boot HART MEDELEG : 0x0000000000f4b509 +[=3hRISC-V EDK2 firmware version 2025.11-3ubuntu7.3 +Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850 +BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr +om PciRoot(0x0)/Pci(0x1,0x0): Not Found +[=3hStarKernel UEFI Loader +Loading kernel from ESP... +[CKPT 001] Entered efi_main - ConOut live +Monolithic build - kernel linked directly +Collecting boot information... +CmdLine: parsed OK +[CKPT 004] Command line parsed +[CKPT 005] Kernel stack allocation decided +[CKPT 006] Boot info collected (ACPI table located) +GOP: linear framebuffer found +[CKPT 007] GOP: linear framebuffer found +[CKPT 008] About to enter ExitBootServices retry loop +Calling kernel_main (monolithic)... +riscv64 item 4.3.5a: satp state at kernel entry (before switch) + satp.MODE = 0x000000000000000a + satp.PPN = 0x00000000000bf868 + __kernel_start = 0x00000000bdea88f8 +riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a) + + + _____ _ _ __ _ + / ____| | | |/ / | | + | (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| | + \___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ | + ____) | || (_| | | | . \ __/ | | | | | __/ | + |_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_| + +LithosAnanke v2.1.0 +Architecture: riscv64 +Build: Oct 6 2026 10:30:10 + +UEFI BootServices: EXITED + +=== StarKernel Boot Information === +Memory map entries: 100 +Total memory: 1020 MB +Usable memory: 977 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 250783 + Free pages : 250131 + Used pages : 652 + Total MB : 979 + Free MB : 977 + Used MB : 2 + +VMM initialized (mapped RAM, CR3 switched) +VMM initialized (mapped RAM, CR3 switched) +VMM self-test: mapped OK at 0xffff800000000000 +VMM self-test complete. + +IDT installed. + +APIC: init... +PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000) +PLIC: init (S-mode context 1, threshold=0) +APIC: init done + +Timer: init... +Timer: RISC-V time CSR @ 10000000 Hz (FALLBACK, no devicetree) +Timer: init done + +Kernel heap initialized. +Heap statistics: + Total bytes: 536870872 + Free bytes: 536870872 + Used bytes: 0 + Peak bytes: 0 + Heap base addr: 2214588416 + Heap end addr: 2751459328 + +Heartbeat: init... +Heartbeat: init done +Kernel initialization complete. +Boot successful! + +StarForth v4: one host node, the F18-derived engine +V4: capsule v4:nucleus-64.f18 signature: missing (unsigned) +PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0xbcc90cc5a638a299 capsule_hash=0xbcc90cc5a638a299 words=298 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x88fb8dcbb051f7b9 +V4: capsule v4:post79.4th signature: missing (unsigned) +PARITY:V4_POST tests=550 pass=550 fail=0 +PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb987dbb4388990bd capsule_hash=0xb987dbb4388990bd dict_hash=0x843716689a31b60a +PARITY:OK +POST: PASSED +ok> : SQ DUP * ; + ok +ok> 7 SQ . 3 4 U* . . +49 0 12 ok +ok> COLD +FORTH-79 Cold Start +System initialized. + ok +ok> 3 4 U* . . +0 12 ok +ok> FORGET U* +Protected word + ERROR +ok> 9 KERNEL-WORD ASK9 ASK9 +Argument out of range + ERROR +ok> BYE +BYE: cold restart diff --git a/v4/Makefile b/v4/Makefile index c42e8d5a..b8194cf6 100644 --- a/v4/Makefile +++ b/v4/Makefile @@ -60,8 +60,16 @@ capsule_dir := -DV4_CAPSULE_DIR='"$(HERE)/capsule"' # capsule/*.v4 -- and writes it as a C file, $(BINDIR)/v4_image_.c. # The hosted system below and the bare-metal kernel (kernel/Makefile, # STARFORTH_V4=1) link the 64-bit one. docs/v4.0.0/NUCLEUS.md. +# The nucleus as a capsule of F18 code (include/v4/capsule.h). The 64-bit one +# is a product's: it goes into the capsule directory, ../capsules/v4, where +# the repository's mkcapsule finds it, hashes it and signs it with every +# other capsule. It is a built file kept in the tree, as +# ../capsules/BLOCK_MAP.md is, and is to be committed when it changes. +ROOT := $(abspath $(HERE)/..) +NUCLEUS_64 := $(ROOT)/capsules/v4/nucleus-64.f18 +NUCLEUS_32 := $(BINDIR)/nucleus-32.f18 HOST_DEFS := -DV4_NODE_WORDS=$(HOST_WORDS) -DV4_DATA_RING=$(HOST_DATA_RING) -DV4_RET_RING=$(HOST_RET_RING) -ENGINE_SRCS := $(addprefix $(HERE)/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c) +ENGINE_SRCS := $(addprefix $(HERE)/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c capsule.c) define IMAGE_RULE $(BINDIR)/mkimage-$(1): $(HERE)/tools/mkimage.c $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/tests/host_map.h $(HERE)/Makefile @@ -69,7 +77,7 @@ $(BINDIR)/mkimage-$(1): $(HERE)/tools/mkimage.c $$(SRCS) $$(wildcard $(HERE)/inc $$(CC) $$(CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=$(1) $(HOST_DEFS) $(capsule_dir) $(HERE)/tools/mkimage.c $$(SRCS) -o $$@ $(BINDIR)/v4_image_$(1).c: $(BINDIR)/mkimage-$(1) $$(CAPSULES) - $(BINDIR)/mkimage-$(1) $$@ + $(BINDIR)/mkimage-$(1) $$@ $$(NUCLEUS_$(1)) endef $(foreach w,$(WIDTHS),$(eval $(call IMAGE_RULE,$(w)))) @@ -85,7 +93,6 @@ image: $(foreach w,$(WIDTHS),$(BINDIR)/v4_image_$(w).c) # SIGN_KEY), and the code that reads it is the kernel's, compiled here as it # is there. The binaries are static, so the two foreign ones run under # user-mode QEMU with nothing else installed. -ROOT := $(abspath $(HERE)/..) HOSTED_ISAS := amd64 aarch64 riscv64 CC_amd64 ?= cc CC_aarch64 ?= aarch64-linux-gnu-gcc @@ -114,7 +121,7 @@ $(BINDIR)/mkcapsule: $(MKCAPSULE_SRCS) @mkdir -p $(BINDIR) cc -std=c99 -Wall -Wextra -O2 -I$(ROOT)/v3/include -I$(ROOT)/kernel/include -I$(ROOT)/tools -o $@ $(MKCAPSULE_SRCS) -$(CAPSULE_DIR_C): $(BINDIR)/mkcapsule $(CAPSULE_FILES) +$(CAPSULE_DIR_C): $(BINDIR)/mkcapsule $(CAPSULE_FILES) $(BINDIR)/v4_image_64.c $(BINDIR)/mkcapsule $(SIGN_KEY_ARGS) $(ROOT)/capsules $@ define HOSTED_RULE diff --git a/v4/README.md b/v4/README.md index 94aa4ca7..5b905187 100644 --- a/v4/README.md +++ b/v4/README.md @@ -29,7 +29,7 @@ under ASan and UBSan. There is no POST and no K measurement yet. | Part | Where | What it is | |---|---|---| | Engine | `v4/src` | The golden model of the 32-opcode node | -| Nucleus | `v4/capsule/*.v4`, built by `v4/tools/mkimage.c` | The assembled words, as a memory image linked into the binary | +| Nucleus | `v4/capsule/*.v4`, built by `v4/tools/mkimage.c` into `capsules/v4/nucleus-64.f18` | The assembled words, as a capsule of F18 code: sent through its port to a node born empty | | Capsules | `capsules/v4/*.4th`, baked by `tools/mkcapsule.c` | FORTH source, loaded when the system comes up | | Boot | `v4/system/boot.c` | Starts the nucleus, checks and loads each capsule, prints the parity lines, gives the prompt | @@ -59,7 +59,7 @@ State, 2026-10-05, after `ENGINE.md` step 1: all six builds start the nucleus, load `v4:forth79.4th`, pass POST and reach `ok>`, with the same lines: ``` -PARITY:V4_NUCLEUS words=298 image_hash=0xd982cf093aadb8b9 +PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=... capsule_hash=0xbcc90cc5a638a299 words=298 PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=... dict_hash=... PARITY:V4_POST tests=550 pass=550 fail=0 PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=... dict_hash=... @@ -68,8 +68,8 @@ POST: PASSED ok> ``` -Bare-metal logs: `logs/20261006-074907/amd64/`, `logs/20261006-075150/aarch64/`, -`logs/20261006-075532/riscv64/`. On each, seven lines were then typed at the +Bare-metal logs: `logs/20261006-102421/amd64/`, `logs/20261006-102706/aarch64/`, +`logs/20261006-103048/riscv64/`. On each, seven lines were then typed at the prompt through the serial port — a definition, its use with the capsule's `U*`, `COLD`, the capsule word again, a `FORGET` of it (refused), a kernel word no one serves, and `BYE` — and each was answered as the hosted binary @@ -105,7 +105,8 @@ everything about word records, the fleet and identity is its alone. making v4 equal v3 on bare metal. Its first part is built and tested in the engine: a node has `V4_PORTS` ports; a read from one blocks until the neighbour writes and a write until the neighbour reads; a node is born -empty with `P` at its ports and executes what a neighbour sends it; and +empty with `P` at its ports and executes what a neighbour sends it, which +is how each product's node now gets its nucleus, as a capsule; and the fabric (`v4/src/fabric.c`) is the nodes there are and the table of how their ports are wired, both of which change while they run. The products below are still one node each; they become the five nodes at `MESH.md` diff --git a/v4/include/v4/boot.h b/v4/include/v4/boot.h index 48d60597..7adabf14 100644 --- a/v4/include/v4/boot.h +++ b/v4/include/v4/boot.h @@ -5,7 +5,7 @@ * (kernel/src/v4/sk_v4.c) both call v4_boot_run and differ only in where * the text goes. So the two start the same way and print the same lines: * - * PARITY:V4_NUCLEUS words=N image_hash=0x... + * PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x... capsule_hash=0x... words=N * PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x... capsule_hash=0x... dict_hash=0x... * PARITY:V4_POST tests=N pass=N fail=N (printed by the POST capsule) * PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0x... capsule_hash=0x... dict_hash=0x... @@ -59,8 +59,8 @@ typedef struct { #define V4_BOOT_KEY_NONE (-1) /* not yet: it is asked again */ #define V4_BOOT_KEY_END (-2) /* and there never will be */ -/* Start the node from the nucleus image, with `disk` as its block storage - * (see v4_image_boot), and load the capsules. Returns 1 with the node idle, +/* A node is born empty, with `disk` as its block storage (v4_image_born); + * it is sent the nucleus through its port; and the capsules are loaded. Returns 1 with the node idle, * waiting to be handed a line; or 0, the failure printed. It prints no * prompt: that is the host's. */ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks); diff --git a/v4/include/v4/capsule.h b/v4/include/v4/capsule.h new file mode 100644 index 00000000..4185c6fa --- /dev/null +++ b/v4/include/v4/capsule.h @@ -0,0 +1,36 @@ +/* capsule.h -- a capsule of F18 code: what a neighbour writes to a node's + * port to put a programme into it and start it. docs/v4.0.0/MESH.md + * section 5. + * + * A node newly born has nothing in it and executes what arrives at its port + * (node.h). So a capsule of F18 code is not a format a node has to + * understand: it is the words themselves, in the order they are sent. For + * each stretch of memory that is not zero, + * + * @p a! @p push the node executes this from its port, + * address takes these two as its literals, + * count - 1 + * @p !+ unext and then this: each word that follows is fetched + * the words ... from the port and stored + * + * and at the end + * + * jump start + * + * This is the F18's own way of loading a node through a port. Nothing here + * knows what the programme is: StarForth's nucleus is one such capsule + * (v4/tools/mkimage.c), and anything written for the node is another. + */ +#ifndef V4_CAPSULE_H +#define V4_CAPSULE_H + +#include "v4/node.h" + +/* Write the capsule that makes a node's memory what `n`'s is, and starts it + * at `start`: `out` is called with each word in turn. Words of `n` that + * are zero are not sent -- a node is born with its memory zero. Returns + * how many words were written. */ +unsigned v4_capsule_write(const v4_node *n, v4_cell start, + void (*out)(void *self, v4_cell word), void *self); + +#endif /* V4_CAPSULE_H */ diff --git a/v4/include/v4/image.h b/v4/include/v4/image.h index 95ecfac2..6d58c524 100644 --- a/v4/include/v4/image.h +++ b/v4/include/v4/image.h @@ -1,31 +1,24 @@ -/* image.h -- a capsule image: the host node's memory with the vocabulary in - * it, and what a loader needs to know to start it. +/* image.h -- the StarForth nucleus, as its host knows it: where things are + * in a node that has taken it in, and how a line is handed to that node. * - * The compiler capsule is text (capsule/ *.v4) and FORTH source (capsule/ - * *.fth). tools/mkimage.c assembles the one and has a node compile the - * other, on the build machine, and writes what the node's memory then holds - * as a C file. A system that is to run the vocabulary -- the hosted binary, - * the bare-metal kernel -- links that file and calls v4_image_boot: it needs - * no assembler, no files and no C library. + * The nucleus is text (capsule/ *.v4). tools/mkimage.c assembles it on the + * build machine and writes two things: * - * An image is for one cell width, one node size and one pair of stack sizes; - * v4_image_boot refuses an image the engine was not built for. + * - the nucleus as a capsule of F18 code (capsule.h), which is put into + * an empty node through its port. It is a capsule like any other: in + * the capsule directory, with a name, a hash and a signature + * (capsules/v4/nucleus-.f18). docs/v4.0.0/MESH.md section 5. + * - this description of it, a C file: the addresses a host needs. + * + * A description is for one cell width, one node size and one pair of stack + * sizes; v4_image_born refuses one the engine was not built for. */ #ifndef V4_IMAGE_H #define V4_IMAGE_H #include "v4/exec.h" -/* one word of memory that is not zero */ typedef struct { - v4_cell addr; - v4_cell value; -} v4_image_cell; - -typedef struct { - const v4_image_cell *cells; /* every non-zero word of memory, in address order */ - unsigned count; - /* what the image was built for */ unsigned cell_bits, node_words, data_ring, ret_ring; @@ -66,7 +59,8 @@ typedef struct { v4_cell boot_cells, fence; } v4_image; -/* The image built from v4/capsule (the generated file defines it). */ +/* The description of the nucleus built from v4/capsule (the generated file + * defines it). */ extern const v4_image v4_capsule_image; /* The system is complete: what is in the dictionary now is what COLD comes @@ -78,11 +72,13 @@ void v4_image_seal(v4_node *n, const v4_image *im); #define V4_LINE_COMPLETED 1 /* the host says " ok" */ #define V4_LINE_ERROR 2 /* the message is printed; the host says " ERROR" */ -/* Reset `n`, load the image into it, attach its registers -- with `disk`, - * `blocks` blocks of 1024 bytes, as its block storage, or none if `disk` is - * 0 -- and leave it idle, waiting to be handed a line. Returns 1, or 0 if - * the image is not for this build of the engine (nothing is then done). */ -int v4_image_boot(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im, +/* A node newly born (node.h), with the registers the nucleus expects of the + * node it runs on -- and `disk`, `blocks` blocks of 1024 bytes, as its block + * storage, or none if `disk` is 0. Its memory is empty and it is blocked + * reading its ports: the nucleus is yet to be sent to it. Returns 1, or 0 + * if the description is not for this build of the engine (nothing is then + * done). */ +int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im, unsigned char *disk, unsigned blocks); /* Hand the node `len` characters of text to interpret. Returns 1, or 0 if diff --git a/v4/src/capsule.c b/v4/src/capsule.c new file mode 100644 index 00000000..6f87a61e --- /dev/null +++ b/v4/src/capsule.c @@ -0,0 +1,55 @@ +/* capsule.c -- a capsule of F18 code. See capsule.h. + * + * Nothing here uses the C library. + */ +#include "v4/capsule.h" +#include "v4/iword.h" +#include "v4/opcode.h" + +/* A stretch of zeros shorter than this, between words that are not zero, is + * sent with them: starting a new stretch costs four words. */ +#define GAP 4u + +static v4_cell words_of(unsigned a, unsigned b, unsigned c, unsigned d) +{ + unsigned op[V4_SLOT_COUNT]; + unsigned k; + op[0] = a; op[1] = b; op[2] = c; op[3] = d; + for (k = 4; k < V4_SLOT_COUNT; k++) op[k] = V4_OP_NOP; + return (v4_cell)v4_iword_assemble(op, 0); +} + +/* `jump addr`, the jump in the first slot so that its address is as wide as + * an address can be */ +static v4_cell jump_to(v4_cell addr) +{ + v4_ucell mask = (v4_ucell)v4_iword_slot_mask(0); + v4_ucell word = (v4_ucell)words_of(V4_OP_JUMP, V4_OP_NOP, V4_OP_NOP, V4_OP_NOP); + return (v4_cell)((word & ~mask) | ((v4_ucell)addr & mask)); +} + +unsigned v4_capsule_write(const v4_node *n, v4_cell start, + void (*out)(void *self, v4_cell word), void *self) +{ + unsigned k = 0, written = 0; + + while (k < V4_NODE_WORDS) { + unsigned first, last, zeros, i; + + if (n->mem[k] == 0) { k++; continue; } + first = last = k; + zeros = 0; + for (i = k + 1; i < V4_NODE_WORDS && zeros < GAP; i++) { + if (n->mem[i] != 0) { last = i; zeros = 0; } else zeros++; + } + out(self, words_of(V4_OP_FETCH_P, V4_OP_BANG_A, V4_OP_FETCH_P, V4_OP_PUSH)); + out(self, (v4_cell)first); + out(self, (v4_cell)(last - first)); + out(self, words_of(V4_OP_FETCH_P, V4_OP_STORE_INC, V4_OP_UNEXT, V4_OP_NOP)); + for (i = first; i <= last; i++) out(self, n->mem[i]); + written += 4u + (last - first + 1u); + k = last + 1; + } + out(self, jump_to(start)); + return written + 1u; +} diff --git a/v4/src/image.c b/v4/src/image.c index a9cd3b85..847b17ce 100644 --- a/v4/src/image.c +++ b/v4/src/image.c @@ -1,34 +1,26 @@ -/* image.c -- start a node from a capsule image. See image.h. +/* image.c -- the StarForth nucleus, as its host knows it. See image.h. * * Nothing here uses the C library: the bare-metal kernel links this file. */ #include "v4/image.h" -int v4_image_boot(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im, +int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im, unsigned char *disk, unsigned blocks) { - unsigned i; - if (im->cell_bits != (unsigned)V4_CELL_BITS || im->node_words != (unsigned)V4_NODE_WORDS || im->data_ring != (unsigned)V4_DATA_RING || im->ret_ring != (unsigned)V4_RET_RING) return 0; - v4_node_reset(n); + v4_node_born(n, im->port); v4_exec_reset(es); v4_heat_reset(h); - for (i = 0; i < im->count; i++) - if (v4_node_addr_ok(im->cells[i].addr)) n->mem[im->cells[i].addr] = im->cells[i].value; v4_node_console_attach(n, im->console_tx); v4_node_console_input_attach(n, im->console_rx, im->console_status); v4_node_stack_regs_attach(n, im->dstack_reg, im->rstack_reg); v4_node_error_attach(n, im->node_error); v4_node_fault_attach(n, im->fault_table); - v4_node_port_attach(n, im->port); if (disk && blocks) v4_node_storage_attach(n, im->storage_reg, disk, blocks); - - n->mem[im->line_status] = V4_LINE_COMPLETED; - n->p = im->idle; return 1; } diff --git a/v4/system/boot.c b/v4/system/boot.c index d75f2cb5..33449540 100644 --- a/v4/system/boot.c +++ b/v4/system/boot.c @@ -80,17 +80,6 @@ uint64_t v4_boot_dict_hash(const v4_node *n, const v4_image *im) return hash_cell(h, n->mem[im->latest]); } -static uint64_t image_hash(const v4_image *im) -{ - uint64_t h = FNV_OFFSET; - unsigned i; - for (i = 0; i < im->count; i++) { - h = hash_cell(h, im->cells[i].addr); - h = hash_cell(h, im->cells[i].value); - } - return h; -} - /* how many words FORTH holds: the list from LATEST, each entry's link in the * cell before its code */ static uint32_t word_count(const v4_node *n, const v4_image *im) @@ -258,6 +247,72 @@ static int load_capsule(const v4_boot *b, const char *name) return 1; } +/* ---- the nucleus ----------------------------------------------------------- */ + +/* The nucleus is a capsule of F18 code (v4/include/v4/capsule.h): the words + * themselves, to be written to the port of a node that has nothing in it. + * The boot is that node's neighbour. It finds the capsule, checks its hash + * and its signature as it does any capsule's, and gives the node a word + * each time it reads its port, until the node has taken them all and is + * where the nucleus waits to be handed a line. */ +#define NUCLEUS_NAME_(bits) "v4:nucleus-" #bits ".f18" +#define NUCLEUS_NAME__(bits) NUCLEUS_NAME_(bits) +#define NUCLEUS_NAME NUCLEUS_NAME__(V4_CELL_BITS) +#define CELL_BYTES (V4_CELL_BITS / 8) + +static int load_nucleus(const v4_boot *b) +{ + const CapsuleDirHeader *dir = capsule_get_directory(); + const CapsuleDesc *descs = capsule_get_descriptors(); + const CapsuleNameEntry *names = capsule_get_names(); + const uint8_t *arena = capsule_get_arena(); + const CapsuleDesc *cap; + const uint8_t *p; + CapsuleValidateResult vr; + CapsuleSigResult sr; + v4_node *n = b->n; + uint64_t words, at = 0, steps = 0; + + cap = capsule_find_by_name(dir, descs, names, NUCLEUS_NAME); + if (!cap) { say(b, "V4: capsule " NUCLEUS_NAME " is not in this binary\n"); return 0; } + vr = capsule_validate(cap, arena, dir->arena_size, 1); + if (vr != CAPSULE_VALID) { + say(b, "V4: capsule " NUCLEUS_NAME ": "); say(b, capsule_validate_result_str(vr)); say(b, "\n"); + return 0; + } + sr = capsule_verify_signature(descs, names, capsule_get_signatures(), arena, dir->desc_count, (int)(cap - descs)); + if (sr != CAPSULE_SIG_OK) { + say(b, "V4: capsule " NUCLEUS_NAME " signature: "); say(b, capsule_sig_result_str(sr)); say(b, "\n"); + if (sr == CAPSULE_SIG_INVALID) return 0; + } + p = capsule_get_payload(cap, arena); + if (!p || cap->length % CELL_BYTES != 0) { say(b, "V4: capsule " NUCLEUS_NAME " is not whole words\n"); return 0; } + words = cap->length / CELL_BYTES; + + while (at < words || !v4_line_done(n, b->im)) { + (void)v4_exec_step_word(n, b->es, b->h); + if (n->stopped || n->asking || ++steps > STEP_LIMIT) { + say(b, "V4: the node did not take the nucleus in\n"); + return 0; + } + if (n->reading && !n->given) { + v4_ucell word = 0; + unsigned i; + if (at >= words) { say(b, "V4: the node wants more than the nucleus holds\n"); return 0; } + for (i = 0; i < CELL_BYTES; i++) word |= (v4_ucell)p[at * CELL_BYTES + i] << (8 * i); + v4_node_port_give(n, 0, (v4_cell)word); + at++; + } + } + + say(b, "PARITY:V4_NUCLEUS name=" NUCLEUS_NAME); + say(b, " capsule_id="); say_hex(b, cap->capsule_id); + say(b, " capsule_hash="); say_hex(b, cap->content_hash); + say(b, " words="); say_dec(b, word_count(n, b->im)); + say(b, "\n"); + return 1; +} + /* ---- the whole boot ------------------------------------------------------ */ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks) @@ -265,13 +320,12 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks) unsigned i; post_len = 0; post_seen = 0; post_clean = 0; - if (!v4_image_boot(b->n, b->es, b->h, b->im, disk, blocks)) { - say(b, "V4: the nucleus image is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n"); + /* a node is born with nothing in it, and is sent the nucleus */ + if (!v4_image_born(b->n, b->es, b->h, b->im, disk, blocks)) { + say(b, "V4: the nucleus is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n"); return 0; } - say(b, "PARITY:V4_NUCLEUS words="); say_dec(b, word_count(b->n, b->im)); - say(b, " image_hash="); say_hex(b, image_hash(b->im)); - say(b, "\n"); + if (!load_nucleus(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; } /* the kernel's words, before any capsule that might use one */ for (i = 0; i < b->word_count; i++) { diff --git a/v4/tests/test_fabric.c b/v4/tests/test_fabric.c index bfd56fe9..d3e7790c 100644 --- a/v4/tests/test_fabric.c +++ b/v4/tests/test_fabric.c @@ -6,6 +6,7 @@ * At the level of opcodes: no word of any vocabulary is defined here. */ #include "v4/asm.h" +#include "v4/capsule.h" #include "v4/fabric.h" #include #include @@ -69,6 +70,13 @@ static int stream_take(void *self, v4_cell value) return 1; } +/* for v4_capsule_write: each word of the capsule, into the stream's own list */ +static void collect(void *self, v4_cell word) +{ + stream *s = (stream *)self; + ((v4_cell *)s->words)[s->count++] = word; +} + /* What a neighbour sends an empty node to put `count` words at `addr` and * start there (MESH.md section 5): for the F18, and for this node, code a * node executes straight from its port. */ @@ -187,6 +195,42 @@ int main(void) CHECK(ND(b)->n.last_from == 0, "it was listening to any port, and knows which it heard from: %u", ND(b)->n.last_from); } + /* ---- a capsule of F18 code: any node's memory, sent to an empty node (capsule.h) ---- */ + { + static v4_node src; + static v4_cell sent[400]; + static stream s; + static v4_device dev; + unsigned k; + + v4_node_reset(&src); + v4_asm_begin(&as, &src, 40); /* a programme, not at the start of memory */ + LIT(123); LIT(OUT); O(BANG_A); O(STORE_A); + wait_for_ever(); + CHECK(v4_asm_ok(&as), "a programme for the capsule"); + src.mem[3] = 5; /* and words here and there: the first of memory, */ + src.mem[200] = -1; src.mem[202] = 7; /* two with one zero between them, */ + src.mem[300] = 9; src.mem[310] = 11; /* two with many, */ + src.mem[PB - 1] = 13; /* and the last before the ports */ + + memset(&s, 0, sizeof s); + s.words = sent; + dev.give = stream_give; dev.take = stream_take; dev.self = &s; + s.count = 0; + s.count = v4_capsule_write(&src, 40, collect, &s); + CHECK(s.count > 0 && s.count < 400, "the capsule is %u words for a memory of %u", s.count, (unsigned)V4_NODE_WORDS); + s.at = 0; + + v4_fabric_init(&f, places, 4); + b = (unsigned)v4_fabric_add(&f, &pool[0], PB); + CHECK(v4_fabric_wire_device(&f, b, 5, &dev), "it is sent to a node newly born"); + steps = settle(5000); + CHECK(s.at == s.count, "which takes all of it: %u of %u", s.at, s.count); + for (k = 0; k < V4_NODE_WORDS && ND(b)->n.mem[k] == (k == (unsigned)OUT ? 123 : src.mem[k]); k++) { } + CHECK(k == V4_NODE_WORDS, "its memory is then the same, word for word (first difference at %u)", k); + CHECK(ND(b)->n.mem[OUT] == 123 && ND(b)->n.reading, "and it was started where the capsule says, and ran"); + } + /* ---- a word is passed on by a node in between ---- */ v4_fabric_init(&f, places, 4); a = loaded(0); diff --git a/v4/tools/mkimage.c b/v4/tools/mkimage.c index 934a6a3d..1a063081 100644 --- a/v4/tools/mkimage.c +++ b/v4/tools/mkimage.c @@ -1,19 +1,21 @@ /* mkimage.c -- build the capsule image (include/v4/image.h). * - * mkimage OUTPUT.c + * mkimage DESCRIPTION.c CAPSULE.f18 * * Runs on the build machine. It assembles capsule/ *.v4 -- the nucleus, * docs/v4.0.0/NUCLEUS.md -- onto a host node with the memory map of - * tests/host_map.h. It runs nothing: it writes every - * word of the node's memory that is not zero, and the addresses a loader - * needs, as a C file. No FORTH source is compiled here: what is not in the - * nucleus is loaded from capsules when the system boots (v4/system/boot.c). + * tests/host_map.h. It runs nothing. It writes the nucleus as a capsule + * of F18 code (v4/include/v4/capsule.h) -- what is sent to an empty node's + * port to make it a StarForth node -- and the addresses a host needs, as a + * C file (v4/include/v4/image.h). No FORTH source is compiled here: what + * is not in the nucleus is loaded from capsules when the system boots. * * The image is the same on every machine it is built on: nothing in it * depends on the build machine but the cell width this tool was compiled * for. */ #include "v4/image.h" +#include "v4/capsule.h" #include "v4/text.h" #include #include @@ -31,6 +33,14 @@ static void die(const char *what, const char *detail) exit(1); } +/* one word of the capsule, to the file */ +static void put_word(void *self, v4_cell word) +{ + v4_ucell u = (v4_ucell)word; + unsigned i; + for (i = 0; i < V4_CELL_BITS / 8; i++) fputc((int)((u >> (8 * i)) & 0xFFu), (FILE *)self); +} + /* every entry from xt back: no access control fields set */ static void clear_acl(v4_cell xt) { @@ -45,7 +55,7 @@ int main(int argc, char **argv) v4_cell k, line, idle, faults, voc; unsigned count = 0; - if (argc != 2) die("usage: mkimage OUTPUT.c", NULL); + if (argc != 3) die("usage: mkimage DESCRIPTION.c CAPSULE.f18", NULL); if (!host_load(&tx, &n, files, (unsigned)(sizeof files / sizeof files[0]))) die("the capsule does not assemble", NULL); if (!v4_text_finish(&tx)) die("not everything is defined", v4_text_error(&tx)); @@ -89,16 +99,19 @@ int main(int argc, char **argv) for (voc = n.mem[VOC_LINK]; voc != 0; voc = n.mem[voc + 1]) clear_acl(n.mem[voc]); if (!v4_node_guards_intact(&n)) die("the node's guards are damaged", NULL); + n.mem[LINE_STATUS] = V4_LINE_COMPLETED; /* idle, with nothing to report */ + + /* the nucleus as a capsule of F18 code: the words a neighbour writes to + * an empty node's port, each in V4_CELL_BITS / 8 bytes, low byte first */ + out = fopen(argv[2], "wb"); + if (!out) die("cannot write", argv[2]); + count = v4_capsule_write(&n, idle, put_word, out); + if (fclose(out) != 0) die("cannot write", argv[2]); + out = fopen(argv[1], "w"); if (!out) die("cannot write", argv[1]); fprintf(out, "/* Generated by v4/tools/mkimage.c from v4/capsule -- do not edit. */\n#include \"v4/image.h\"\n\n"); - fprintf(out, "static const v4_image_cell cells[] = {\n"); - for (k = 0; k < (v4_cell)V4_NODE_WORDS; k++) - if (n.mem[k] != 0) { - fprintf(out, " { %ld, (v4_cell)0x%llxULL },\n", (long)k, (unsigned long long)(v4_ucell)n.mem[k]); - count++; - } - fprintf(out, "};\n\nconst v4_image v4_capsule_image = {\n cells, %uu,\n %uu, %uu, %uu, %uu,\n", count, + fprintf(out, "const v4_image v4_capsule_image = {\n %uu, %uu, %uu, %uu,\n", (unsigned)V4_CELL_BITS, (unsigned)V4_NODE_WORDS, (unsigned)V4_DATA_RING, (unsigned)V4_RET_RING); fprintf(out, " %ld, %ld, %ld,\n %ld, %uu, %ld,\n %ld, %ld,\n %ld, %ld,\n", (long)line, (long)idle, (long)LINE_STATUS, (long)TIB, 1025u, (long)SPAN, (long)faults, (long)PORT, (long)w_key, (long)w_key_end); @@ -106,6 +119,6 @@ int main(int argc, char **argv) (long)DSTACK_REG, (long)RSTACK_REG, (long)NODE_ERROR, (long)STORAGE_REG, (long)DP, (long)LATEST, (long)BOOT_CELLS, (long)FENCE); if (fclose(out) != 0) die("cannot write", argv[1]); - fprintf(stderr, "mkimage: %u words of memory, dictionary to word %ld, %d-bit cells -> %s\n", count, (long)((n.mem[DP] + 3) / 4), V4_CELL_BITS, argv[1]); + fprintf(stderr, "mkimage: a capsule of %u words, dictionary to word %ld, %d-bit cells -> %s, %s\n", count, (long)((n.mem[DP] + 3) / 4), V4_CELL_BITS, argv[2], argv[1]); return 0; }