diff --git a/capsules/BLOCK_MAP.md b/capsules/BLOCK_MAP.md index a8037619..d36fc6c4 100644 --- a/capsules/BLOCK_MAP.md +++ b/capsules/BLOCK_MAP.md @@ -1,5 +1,5 @@ # Capsule Block Manifest — Auto-generated - + @@ -30,7 +30,7 @@ | `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | n/a | | `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | n/a | | `v4:forth79.4th` | 6000, 6001, 6002 | `0x4055641ee17d176b` | n/a | -| `v4:post79.4th` | 7000, 7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7011, 7012, 7013, 7014, 7015, 7016, 7017, 7018, 7019, 7020, 7021, 7022, 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, 7031, 7032, 7033, 7034, 7035, 7036, 7037, 7038, 7039, 7040, 7041, 7042, 7043, 7044, 7045, 7046, 7047, 7048, 7049, 7050, 7051, 7052, 7053, 7054, 7055, 7056, 7057, 7058, 7059, 7060, 7061, 7062, 7063 | `0xb987dbb4388990bd` | n/a | +| `v4:post79.4th` | 7000, 7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7011, 7012, 7013, 7014, 7015, 7016, 7017, 7018, 7019, 7020, 7021, 7022, 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, 7031, 7032, 7033, 7034, 7035, 7036, 7037, 7038, 7039, 7040, 7041, 7042, 7043, 7044, 7045, 7046, 7047, 7048, 7049, 7050, 7051, 7052, 7053, 7054, 7055, 7056, 7057, 7058, 7059, 7060, 7061, 7062, 7063 | `0xb26d1b974884179d` | n/a | | `workload-0.4th` | 2200, 2201 | `0x93f86f60aeba8feb` | n/a | | `workload-1-lite.4th` | 5058, 5059 | `0x44a7a7e3176dcc8d` | n/a | | `workload-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` | n/a | @@ -371,70 +371,70 @@ | 6000 | `v4:forth79.4th` | `0x4055641ee17d176b` | ok | | 6001 | `v4:forth79.4th` | `0x4055641ee17d176b` | ok | | 6002 | `v4:forth79.4th` | `0x4055641ee17d176b` | ok | -| 7000 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7001 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7002 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7003 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7004 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7005 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7006 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7007 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7008 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7009 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7010 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7011 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7012 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7013 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7014 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7015 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7016 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7017 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7018 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7019 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7020 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7021 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7022 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7023 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7024 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7025 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7026 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7027 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7028 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7029 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7030 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7031 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7032 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7033 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7034 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7035 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7036 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7037 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7038 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7039 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7040 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7041 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7042 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7043 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7044 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7045 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7046 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7047 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7048 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7049 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7050 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7051 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7052 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7053 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7054 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7055 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7056 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7057 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7058 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7059 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7060 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7061 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7062 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | -| 7063 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok | +| 7000 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7001 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7002 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7003 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7004 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7005 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7006 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7007 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7008 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7009 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7010 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7011 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7012 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7013 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7014 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7015 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7016 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7017 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7018 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7019 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7020 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7021 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7022 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7023 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7024 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7025 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7026 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7027 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7028 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7029 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7030 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7031 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7032 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7033 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7034 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7035 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7036 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7037 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7038 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7039 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7040 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7041 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7042 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7043 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7044 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7045 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7046 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7047 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7048 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7049 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7050 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7051 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7052 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7053 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7054 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7055 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7056 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7057 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7058 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7059 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7060 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7061 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7062 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | +| 7063 | `v4:post79.4th` | `0xb26d1b974884179d` | ok | ## Conflicts diff --git a/capsules/v4/nucleus-64.f18 b/capsules/v4/nucleus-64.f18 index 229ddeac..84d109da 100644 Binary files a/capsules/v4/nucleus-64.f18 and b/capsules/v4/nucleus-64.f18 differ diff --git a/capsules/v4/post79.4th b/capsules/v4/post79.4th index e9deb33c..882b9225 100644 --- a/capsules/v4/post79.4th +++ b/capsules/v4/post79.4th @@ -296,35 +296,16 @@ T| 456 HERE ! HERE @ HERE @ = . CR T{ @.empty_stack T| @ ->ERR -T{ C!.basic -T| 65 HERE C! HERE C@ . CR --> }T 4 1660649 T= -T{ C!.zero -T| 0 HERE C! HERE C@ . CR --> }T 3 47130 T= -T{ C!.high_byte -T| 255 HERE C! HERE C@ . CR --> }T 5 14252476 T= -Block 7020 -T{ C!.truncation -T| 256 HERE C! HERE C@ . CR --> }T 3 47130 T= -T{ C@.after_cstore -T| 97 HERE C! HERE C@ . CR --> }T 4 1751944 T= -T{ C@.zero_byte -T| 0 HERE C! HERE C@ . CR --> }T 3 47130 T= T{ ,.basic T| 42 , HERE 1 - @ . CR -> }T 4 1598184 T= T{ ,.negative T| -999 , HERE 1 - @ . CR -> }T 6 529078 T= -Block 7021 T{ HERE.basic T| HERE HERE = . CR -> }T 4 1388686 T= +Block 7020 T{ HERE.after_comma T| HERE 42 , HERE SWAP - 1 CELLS = 0 SWAP / -> 0 }T 0 0 T= @@ -337,10 +318,10 @@ T| HERE 0 ALLOT HERE SWAP - . CR T{ ALLOT.negative T| HERE -4 ALLOT HERE SWAP - . CR -> }T 4 1391569 T= -Block 7022 T{ PAD.basic T| PAD PAD = . CR -> }T 4 1388686 T= +Block 7021 T{ PAD.different_from_here T| PAD HERE = . CR -> }T 3 47130 T= @@ -353,23 +334,16 @@ T| 42 HERE ! 0 HERE +! HERE @ . CR T{ +!.negative T| 10 HERE ! -3 HERE +! HERE @ . CR -> }T 3 53857 T= -Block 7023 T{ +!.accumulate T| 0 HERE ! 1 HERE +! 2 HERE +! 3 HERE +! HERE @ . CR -> }T 3 52896 T= +Block 7022 T{ +!.empty_stack T| +! ->ERR -T{ FILL.basic -T| HERE 8 65 FILL HERE C@ . HERE 1+ C@ . CR --> }T 7 14524330 T= T{ FILL.zero_n T| HERE 0 88 FILL -> }T 0 0 T= -T{ FILL.zero_byte -T| HERE 4 0 FILL HERE C@ . CR --> }T 3 47130 T= -Block 7024 T{ FILL.empty_stack T| FILL ->ERR @@ -380,13 +354,13 @@ T| HERE 17 + @ . CR T{ MOVE.zero_n T| HERE HERE 8 + 0 MOVE -> }T 0 0 T= +Block 7023 T{ MOVE.empty_stack T| MOVE ->ERR T{ +.basic T| 5 7 + . CR -> }T 4 1508811 T= -Block 7025 T{ +.zero_add T| 42 0 + . CR -> }T 4 1598184 T= @@ -396,13 +370,13 @@ T| -5 3 + . CR T{ +.both_negative T| -5 -3 + . CR -> }T 4 1395413 T= +Block 7024 T{ +.overflow T| 2147483647 1 + . CR -> }T 12 4849879 T= T{ +.underflow T| -2147483648 -1 + . CR -> }T 13 5384517 T= -Block 7026 T{ +.empty_stack T| + ->ERR @@ -412,13 +386,13 @@ T| 42 + T{ -.basic T| 10 3 - . CR -> }T 3 53857 T= +Block 7025 T{ -.zero_sub T| 42 0 - . CR -> }T 4 1598184 T= T{ -.from_zero T| 0 5 - . CR -> }T 4 1392530 T= -Block 7027 T{ -.negative T| 5 -3 - . CR -> }T 3 54818 T= @@ -428,13 +402,13 @@ T| 42 42 - . CR T{ -.overflow T| -2147483648 1 - . CR -> }T 13 5384517 T= +Block 7026 T{ -.underflow T| 2147483647 -1 - . CR -> }T 12 4849879 T= T{ -.empty_stack T| - ->ERR -Block 7028 T{ -.one_item T| 42 - ->ERR @@ -444,13 +418,13 @@ T| 6 7 * . CR T{ *.by_zero T| 42 0 * . CR -> }T 3 47130 T= +Block 7027 T{ *.by_one T| 42 1 * . CR -> }T 4 1598184 T= T{ *.by_negative T| 6 -7 * . CR -> }T 5 9602197 T= -Block 7029 T{ *.negative_negative T| -6 -7 * . CR -> }T 4 1598184 T= @@ -460,13 +434,13 @@ T| 32767 2 * . CR T{ *.overflow T| 65536 65536 * . CR -> }T 12 6984600 T= +Block 7028 T{ *.empty_stack T| * ->ERR T{ *.one_item T| 42 * ->ERR -Block 7030 T{ /.basic T| 15 3 / . CR -> }T 3 51935 T= @@ -476,13 +450,13 @@ T| 42 1 / . CR T{ /.negative_dividend T| -15 3 / . CR -> }T 4 1392530 T= +Block 7029 T{ /.negative_divisor T| 15 -3 / . CR -> }T 4 1392530 T= T{ /.both_negative T| -15 -3 / . CR -> }T 3 51935 T= -Block 7031 T{ /.truncation T| 7 3 / . CR -> }T 3 49052 T= @@ -492,13 +466,13 @@ T| 42 0 / T{ /.empty_stack T| / ->ERR +Block 7030 T{ /.one_item T| 42 / ->ERR T{ MOD.basic T| 17 5 MOD . CR -> }T 3 49052 T= -Block 7032 T{ MOD.exact_division T| 15 3 MOD . CR -> }T 3 47130 T= @@ -508,13 +482,13 @@ T| 42 1 MOD . CR T{ MOD.negative_dividend T| -17 5 MOD . CR -> }T 4 1389647 T= +Block 7031 T{ MOD.negative_divisor T| 17 -5 MOD . CR -> }T 3 49052 T= T{ MOD.small_by_large T| 3 7 MOD . CR -> }T 3 50013 T= -Block 7033 T{ MOD.by_zero T| 42 0 MOD ->ERR @@ -524,13 +498,13 @@ T| MOD T{ MOD.one_item T| 42 MOD ->ERR +Block 7032 T{ /MOD.basic T| 17 5 /MOD . . CR -> }T 5 14547503 T= T{ /MOD.exact T| 15 3 /MOD . . CR -> }T 5 16392623 T= -Block 7034 T{ /MOD.negative T| -17 5 /MOD . . CR -> }T 7 5748361 T= @@ -540,13 +514,13 @@ T| 42 0 /MOD T{ /MOD.empty_stack T| /MOD ->ERR +Block 7033 T{ /MOD.one_item T| 42 /MOD ->ERR T{ ABS.positive T| 42 ABS . CR -> }T 4 1598184 T= -Block 7035 T{ ABS.negative T| -42 ABS . CR -> }T 4 1598184 T= @@ -556,13 +530,13 @@ T| 0 ABS . CR T{ ABS.min_int T| -2147483648 ABS . CR -> }T 12 4849879 T= +Block 7034 T{ ABS.empty_stack T| ABS ->ERR T{ NEGATE.positive T| 42 NEGATE . CR -> }T 5 9602197 T= -Block 7036 T{ NEGATE.negative T| -42 NEGATE . CR -> }T 4 1598184 T= @@ -572,13 +546,13 @@ T| 0 NEGATE . CR T{ NEGATE.min_int T| -2147483648 NEGATE . CR -> }T 12 4849879 T= +Block 7035 T{ NEGATE.empty_stack T| NEGATE ->ERR T{ MIN.basic T| 5 3 MIN . CR -> }T 3 50013 T= -Block 7037 T{ MIN.equal T| 42 42 MIN . CR -> }T 4 1598184 T= @@ -588,13 +562,13 @@ T| -5 -3 MIN . CR T{ MIN.mixed T| -1 1 MIN . CR -> }T 4 1388686 T= +Block 7036 T{ MIN.one_item T| 42 MIN ->ERR T{ MIN.empty_stack T| MIN ->ERR -Block 7038 T{ MAX.basic T| 5 3 MAX . CR -> }T 3 51935 T= @@ -604,13 +578,13 @@ T| 42 42 MAX . CR T{ MAX.negative T| -5 -3 MAX . CR -> }T 4 1390608 T= +Block 7037 T{ MAX.mixed T| -1 1 MAX . CR -> }T 3 48091 T= T{ MAX.one_item T| 42 MAX ->ERR -Block 7039 T{ MAX.empty_stack T| MAX ->ERR @@ -620,13 +594,13 @@ T| 5 1+ . CR T{ 1+.zero T| 0 1+ . CR -> }T 3 48091 T= +Block 7038 T{ 1+.negative T| -1 1+ . CR -> }T 3 47130 T= T{ 1+.chain T| 3 1+ 1+ 1+ . CR -> }T 3 52896 T= -Block 7040 T{ 1+.overflow T| 2147483647 1+ . CR -> }T 12 4849879 T= @@ -636,13 +610,13 @@ T| 1+ T{ 1-.basic T| 5 1- . CR -> }T 3 50974 T= +Block 7039 T{ 1-.to_zero T| 1 1- . CR -> }T 3 47130 T= T{ 1-.to_negative T| 0 1- . CR -> }T 4 1388686 T= -Block 7041 T{ 1-.chain T| 6 1- 1- 1- . CR -> }T 3 50013 T= @@ -652,13 +626,13 @@ T| -2147483648 1- . CR T{ 1-.empty_stack T| 1- ->ERR +Block 7040 T{ 2+.basic T| 5 2+ . CR -> }T 3 53857 T= T{ 2+.zero T| 0 2+ . CR -> }T 3 49052 T= -Block 7042 T{ 2+.negative T| -3 2+ . CR -> }T 4 1388686 T= @@ -668,13 +642,13 @@ T| 2+ T{ 2-.basic T| 7 2- . CR -> }T 3 51935 T= +Block 7041 T{ 2-.zero T| 2 2- . CR -> }T 3 47130 T= T{ 2-.negative T| 0 2- . CR -> }T 4 1389647 T= -Block 7043 T{ 2-.empty_stack T| 2- ->ERR @@ -684,13 +658,13 @@ T| -1 -1 AND . CR T{ AND.first_false T| 0 -1 AND . CR -> }T 3 47130 T= +Block 7042 T{ AND.second_false T| -1 0 AND . CR -> }T 3 47130 T= T{ AND.both_false T| 0 0 AND . CR -> }T 3 47130 T= -Block 7044 T{ AND.bitwise T| 85 51 AND . CR -> }T 4 1513616 T= @@ -700,13 +674,13 @@ T| AND T{ AND.one_item T| 42 AND ->ERR +Block 7043 T{ OR.both_true T| -1 -1 OR . CR -> }T 4 1388686 T= T{ OR.first_false T| 0 -1 OR . CR -> }T 4 1388686 T= -Block 7045 T{ OR.second_false T| -1 0 OR . CR -> }T 4 1388686 T= @@ -716,13 +690,13 @@ T| 0 0 OR . CR T{ OR.bitwise T| 85 51 OR . CR -> }T 5 13213635 T= +Block 7044 T{ OR.empty_stack T| OR ->ERR T{ OR.one_item T| 42 OR ->ERR -Block 7046 T{ XOR.both_true T| -1 -1 XOR . CR -> }T 3 47130 T= @@ -732,13 +706,13 @@ T| 0 -1 XOR . CR T{ XOR.second_false T| -1 0 XOR . CR -> }T 4 1388686 T= +Block 7045 T{ XOR.both_false T| 0 0 XOR . CR -> }T 3 47130 T= T{ XOR.bitwise T| 85 51 XOR . CR -> }T 5 13177117 T= -Block 7047 T{ XOR.empty_stack T| XOR ->ERR @@ -748,13 +722,13 @@ T| 42 XOR T{ NOT.true T| -1 NOT . CR -> }T 3 47130 T= +Block 7046 T{ NOT.false T| 0 NOT . CR -> }T 4 1388686 T= T{ NOT.positive T| 42 NOT . CR -> }T 3 47130 T= -Block 7048 T{ NOT.negative T| -42 NOT . CR -> }T 3 47130 T= @@ -764,13 +738,13 @@ T| NOT T{ =.equal T| 42 42 = . CR -> }T 4 1388686 T= +Block 7047 T{ =.not_equal T| 42 43 = . CR -> }T 3 47130 T= T{ =.zero_equal T| 0 0 = . CR -> }T 4 1388686 T= -Block 7049 T{ =.negative_equal T| -42 -42 = . CR -> }T 4 1388686 T= @@ -780,13 +754,13 @@ T| -42 42 = . CR T{ =.empty_stack T| = ->ERR +Block 7048 T{ =.one_item T| 42 = ->ERR T{ <.less_than T| 5 7 < . CR -> }T 4 1388686 T= -Block 7050 T{ <.greater_than T| 7 5 < . CR -> }T 3 47130 T= @@ -796,13 +770,13 @@ T| 5 5 < . CR T{ <.negative_positive T| -5 5 < . CR -> }T 4 1388686 T= +Block 7049 T{ <.negative_negative T| -7 -5 < . CR -> }T 4 1388686 T= T{ <.empty_stack T| < ->ERR -Block 7051 T{ <.one_item T| 42 < ->ERR @@ -812,13 +786,13 @@ T| 7 5 > . CR T{ >.less_than T| 5 7 > . CR -> }T 3 47130 T= +Block 7050 T{ >.equal T| 5 5 > . CR -> }T 3 47130 T= T{ >.positive_negative T| 5 -5 > . CR -> }T 4 1388686 T= -Block 7052 T{ >.negative_negative T| -5 -7 > . CR -> }T 4 1388686 T= @@ -828,13 +802,13 @@ T| > T{ >.one_item T| 42 > ->ERR +Block 7051 T{ 0=.zero T| 0 0= . CR -> }T 4 1388686 T= T{ 0=.positive T| 42 0= . CR -> }T 3 47130 T= -Block 7053 T{ 0=.negative T| -42 0= . CR -> }T 3 47130 T= @@ -844,13 +818,13 @@ T| 0= T{ 0<.negative T| -42 0< . CR -> }T 4 1388686 T= +Block 7052 T{ 0<.zero T| 0 0< . CR -> }T 3 47130 T= T{ 0<.positive T| 42 0< . CR -> }T 3 47130 T= -Block 7054 T{ 0<.empty_stack T| 0< ->ERR @@ -860,13 +834,13 @@ T| 42 0> . CR T{ 0>.zero T| 0 0> . CR -> }T 3 47130 T= +Block 7053 T{ 0>.negative T| -42 0> . CR -> }T 3 47130 T= T{ 0>.empty_stack T| 0> ->ERR -Block 7055 T{ U<.less T| 3 5 U< . CR -> }T 4 1388686 T= @@ -876,13 +850,13 @@ T| 5 5 U< . CR T{ U<.greater T| 7 5 U< . CR -> }T 3 47130 T= +Block 7054 T{ U<.zero_one T| 0 1 U< . CR -> }T 4 1388686 T= T{ U<.empty_stack T| U< ->ERR -Block 7056 T{ */.basic T| 6 7 4 */ . CR -> }T 4 1506889 T= @@ -892,13 +866,13 @@ T| 12 5 3 */ . CR T{ */.zero_multiply T| 0 999 123 */ . CR -> }T 3 47130 T= +Block 7055 T{ */.by_one T| 42 5 1 */ . CR -> }T 5 14128507 T= T{ */.truncation T| 7 3 2 */ . CR -> }T 4 1506889 T= -Block 7057 T{ */.negative_multiply T| -6 7 4 */ . CR -> }T 5 9510902 T= @@ -908,13 +882,13 @@ T| 6 7 -4 */ . CR T{ */.div_by_zero T| 6 7 0 */ ->ERR +Block 7056 T{ */.empty_stack T| */ ->ERR T{ */.two_items T| 1 2 */ ->ERR -Block 7058 T{ */MOD.basic T| 17 3 5 */MOD . . CR -> }T 6 5318234 T= @@ -924,13 +898,13 @@ T| 15 4 3 */MOD . . CR T{ */MOD.zero_multiply T| 0 999 123 */MOD . . CR -> }T 5 11775018 T= +Block 7057 T{ */MOD.by_one T| 42 5 1 */MOD . . CR -> }T 7 4765003 T= T{ */MOD.remainder T| 7 8 5 */MOD . . CR -> }T 6 6241755 T= -Block 7059 T{ */MOD.negative T| -7 3 4 */MOD . . CR -> }T 7 12674054 T= @@ -940,13 +914,13 @@ T| 6 7 0 */MOD T{ */MOD.empty_stack T| */MOD ->ERR +Block 7058 T{ D+.basic T| 100 200 300 400 D+ . . CR -> }T 9 6378548 T= T{ D+.zero_add T| 42 99 0 0 D+ . . CR -> }T 7 6471656 T= -Block 7060 T{ D+.negative T| -100 -200 50 75 D+ . . CR -> }T 10 13971193 T= @@ -956,13 +930,13 @@ T| 2147483647 0 1 0 D+ . . CR T{ D+.both_negative T| -100 -200 -300 -400 D+ . . CR -> }T 11 5003369 T= +Block 7059 T{ D+.three_items T| 1 2 3 D+ ->ERR T{ D+.empty_stack T| D+ ->ERR -Block 7061 T{ DNEGATE.positive T| 100 200 DNEGATE . . CR -> }T 11 13922440 T= @@ -972,13 +946,13 @@ T| -100 -200 DNEGATE . . CR T{ DNEGATE.zero T| 0 0 DNEGATE . . CR -> }T 5 11775018 T= +Block 7060 T{ DNEGATE.max_positive T| 2147483647 0 DNEGATE . . CR -> }T 16 11447367 T= T{ DNEGATE.one_item T| 42 DNEGATE ->ERR -Block 7062 T{ DNEGATE.empty_stack T| DNEGATE ->ERR @@ -988,13 +962,13 @@ T| 100 200 300 400 D< . CR T{ D<.greater_than T| 300 400 100 200 D< . CR -> }T 3 47130 T= +Block 7061 T{ D<.equal T| 100 200 100 200 D< . CR -> }T 3 47130 T= T{ D<.negative_positive T| -100 -1 100 0 D< . CR -> }T 4 1388686 T= -Block 7063 T{ D<.negative_negative T| -300 -1 -100 -1 D< . CR -> }T 4 1388686 T= @@ -1004,13 +978,13 @@ T| 100 200 100 300 D< . CR T{ D<.three_items T| 1 2 3 D< ->ERR +Block 7062 T{ D<.empty_stack T| D< ->ERR T{ BASE.decimal T| DECIMAL 42 . CR -> }T 4 1598184 T= -Block 7064 T{ BASE.hex T| HEX FF . CR -> }T 4 2153642 T= @@ -1020,13 +994,13 @@ T| OCTAL 52 . CR T{ BASE.base_store T| 16 BASE ! 255 . CR -> }T 5 14252476 T= +Block 7063 T{ BASE.base_fetch T| BASE @ . CR -> }T 4 1506889 T= T{ DECIMAL.from_hex T| HEX FF DECIMAL . CR -> }T 5 14252476 T= -Block 7065 T{ DECIMAL.from_octal T| OCTAL 52 DECIMAL . CR -> }T 4 1598184 T= @@ -1036,13 +1010,13 @@ T| DECIMAL 42 . CR 42 . CR HEX 42 . CR T{ <#.basic T| DECIMAL 42 S>D <# #S #> TYPE CR -> }T 3 51532 T= +Block 7064 T{ <#.empty T| 0 0 <# #> TYPE CR -> }T 1 10 T= T{ <#.negative T| -42 DUP ABS 0 <# #S ROT SIGN #> TYPE CR -> }T 4 1392127 T= -Block 7066 T{ #.single_digit T| 15 0 <# # #> TYPE CR -> }T 2 1653 T= @@ -1052,13 +1026,13 @@ T| 15 0 <# # # #> TYPE CR T{ #.zero_pad T| 5 0 <# # # #> TYPE CR -> }T 3 47781 T= +Block 7065 T{ #S.basic T| 42 0 <# #S #> TYPE CR -> }T 3 51532 T= T{ #S.zero T| 0 0 <# #S #> TYPE CR -> }T 2 1498 T= -Block 7067 T{ #S.large T| 1234567890 0 <# #S #> TYPE CR -> }T 11 7265487 T= @@ -1068,13 +1042,13 @@ T| -42 DUP ABS 0 <# #S ROT SIGN #> TYPE CR T{ SIGN.positive T| 42 DUP ABS 0 <# #S ROT SIGN #> TYPE CR -> }T 3 51532 T= +Block 7066 T{ SIGN.zero T| 0 DUP ABS 0 <# #S ROT SIGN #> TYPE CR -> }T 2 1498 T= T{ #>.normal T| 42 0 <# #S #> TYPE CR -> }T 3 51532 T= -Block 7068 T{ #>.empty T| 0 0 <# #> TYPE CR -> }T 1 10 T= @@ -1084,29 +1058,26 @@ T| 42 0 <# #S #> SWAP DROP . CR T{ HOLD.basic T| 42 0 <# 46 HOLD #S #> TYPE CR -> }T 4 1598618 T= +Block 7067 T{ HOLD.overflow T| <# 257 HOLD #> ->ERR T{ COUNT.basic T| 4 PAD C! PAD COUNT . PAD - . CR -> }T 5 15470063 T= -Block 7069 T{ COUNT.empty T| 0 PAD C! PAD COUNT . PAD - . CR -> }T 5 11775979 T= T{ COUNT.max_length T| 255 PAD C! PAD COUNT . PAD - . CR -> }T 7 6459661 T= -T{ COUNT.string_bounds -T| HERE COUNT SWAP 1- = . CR --> }T 3 47130 T= T{ COUNT.empty_stack T| COUNT ->ERR +Block 7068 T{ -TRAILING.basic T| HERE S" Test " -TRAILING TYPE CR -> 1 }TD 6 6975864 T= -Block 7070 T{ -TRAILING.all_spaces T| HERE S" " -TRAILING TYPE CR -> 1 }TD 1 10 T= @@ -1120,13 +1091,10 @@ T{ CMOVE.basic T| 65 PAD C! 66 PAD 1+ C! PAD PAD 10 + 2 CMOVE PAD 10 + C@ . PAD T| 11 + C@ . CR -> }T 7 14525291 T= +Block 7069 T{ CMOVE.empty T| HERE HERE 10 + 0 CMOVE -> }T 0 0 T= -Block 7071 -T{ CMOVE.overlap -T| HERE DUP 1+ 5 CMOVE --> }T 0 0 T= T{ >IN.initial T| >IN @ . CR -> }T 3 55779 T= @@ -1139,7 +1107,7 @@ T| 42 . CR T{ ..negative T| -42 . CR -> }T 5 9602197 T= -Block 7072 +Block 7070 T{ ..zero T| 0 . CR -> }T 3 47130 T= @@ -1155,7 +1123,7 @@ T| 1 2 3 . . . CR T{ ..empty_stack T| . ->ERR -Block 7073 +Block 7071 T{ CR.basic T| 42 . CR 43 . CR -> }T 8 16061841 T= @@ -1171,7 +1139,7 @@ T| 1 2 3 . . . CR T{ SPACE.basic T| 42 . SPACE 43 . CR -> }T 8 2824871 T= -Block 7074 +Block 7072 T{ SPACE.multiple T| SPACE SPACE SPACE -> }T 3 31776 T= @@ -1187,7 +1155,7 @@ T| 0 SPACES T{ SPACES.one T| 1 SPACES -> }T 1 32 T= -Block 7075 +Block 7073 T{ SPACES.multiple T| 5 SPACES -> }T 5 13760544 T= @@ -1203,7 +1171,7 @@ T| 42 . 3 SPACES 43 . CR T{ SPACES.empty_stack T| SPACES ->ERR -Block 7076 +Block 7074 T{ EMIT.letter_A T| 65 EMIT CR -> }T 2 2025 T= @@ -1219,7 +1187,7 @@ T| 32 EMIT CR T{ EMIT.newline T| 10 EMIT -> }T 1 10 T= -Block 7077 +Block 7075 T{ EMIT.high_ascii T| 127 EMIT CR -> }T 2 3947 T= @@ -1235,26 +1203,23 @@ T| 72 EMIT 73 EMIT 10 EMIT T{ EMIT.empty_stack T| EMIT ->ERR -Block 7078 +Block 7076 T{ TYPE.basic_string T| 72 PAD C! 105 PAD 1+ C! PAD 2 TYPE CR -> }T 3 72457 T= T{ TYPE.empty_string T| HERE 0 TYPE CR -> }T 1 10 T= -T{ TYPE.single_char -T| HERE 65 OVER C! 1 TYPE CR --> }T 2 2025 T= T{ TYPE.numbers T| 49 PAD C! 50 PAD 1+ C! PAD 2 TYPE CR -> }T 3 48649 T= T{ TYPE.zero_length T| PAD 0 TYPE -> }T 0 0 T= -Block 7079 T{ TYPE.one_item T| PAD TYPE ->ERR +Block 7077 T{ TYPE.empty_stack T| TYPE ->ERR @@ -1267,13 +1232,10 @@ T| 0 PAD C! PAD COUNT . PAD - . CR T{ COUNT.max_length~2 T| 255 PAD C! PAD COUNT . PAD - . CR -> }T 7 6459661 T= -Block 7080 -T{ COUNT.zero_addr_plus_one -T| HERE COUNT SWAP 1- = . CR --> }T 3 47130 T= T{ COUNT.empty_stack~2 T| COUNT ->ERR +Block 7078 T{ WORD.empty_stack T| WORD ->ERR @@ -1283,13 +1245,13 @@ T| 1 BLOCK DUP . CR T{ BLOCK.zero_block T| 0 BLOCK ->ERR -Block 7081 T{ BLOCK.large_block T| 65536 BLOCK ->ERR T{ BLOCK.negative T| -1 BLOCK ->ERR +Block 7079 T{ BLOCK.empty_stack T| BLOCK ->ERR @@ -1299,13 +1261,13 @@ T| 1 BUFFER DUP . CR T{ BUFFER.zero_block T| 0 BUFFER ->ERR -Block 7082 T{ BUFFER.flush_dirty T| 2 BLOCK 1+ 2 BUFFER -> 2 }TD 0 0 T= T{ BUFFER.empty_stack T| BUFFER ->ERR +Block 7080 T{ UPDATE.basic T| 1 BLOCK UPDATE -> 1 }TD 0 0 T= @@ -1315,13 +1277,13 @@ T| 1 BLOCK UPDATE UPDATE T{ UPDATE.no_block T| 0 SCR ! UPDATE -> }T 0 0 T= -Block 7083 T{ SAVE-BUFFERS.basic T| SAVE-BUFFERS -> }T 0 0 T= T{ SAVE-BUFFERS.dirty_blocks T| 1 BLOCK UPDATE SAVE-BUFFERS -> 1 }TD 0 0 T= +Block 7081 T{ SAVE-BUFFERS.no_dirty T| SAVE-BUFFERS -> }T 0 0 T= @@ -1331,13 +1293,13 @@ T| EMPTY-BUFFERS T{ EMPTY-BUFFERS.after_use T| 1 BLOCK EMPTY-BUFFERS -> 1 }TD 0 0 T= -Block 7084 T{ EMPTY-BUFFERS.dirty_blocks T| 1 BLOCK UPDATE EMPTY-BUFFERS -> 1 }TD 0 0 T= T{ LOAD.basic T| 1 LOAD -> }T 0 0 T= +Block 7082 T{ LOAD.zero_block T| 0 LOAD ->ERR @@ -1347,13 +1309,13 @@ T| 9999 LOAD T{ LOAD.empty_stack T| LOAD ->ERR -Block 7085 T{ LIST.basic T| 1 LIST -> 0 }TD T{ LIST.zero_block T| 0 LIST ->ERR +Block 7083 T{ LIST.nonexistent T| 9999 LIST ->ERR @@ -1363,13 +1325,13 @@ T| LIST T{ SCR.basic T| SCR @ . CR -> }T 3 48091 T= -Block 7086 T{ SCR.after_list T| 1 LIST SCR @ . CR -> 0 }TD T{ SCR.after_load T| 1 LOAD SCR @ . CR -> }T 3 48091 T= +Block 7084 T{ HERE.basic~2 T| HERE HERE = . CR -> }T 4 1388686 T= @@ -1379,13 +1341,13 @@ T| HERE 42 , HERE SWAP - 1 CELLS = 0 SWAP / T{ HERE.after_c_comma T| HERE 65 C, HERE SWAP - . CR -> }T 3 48091 T= -Block 7087 T{ HERE.after_allot T| HERE 10 ALLOT HERE SWAP - . CR -> }T 4 1506889 T= T{ HERE.stability T| HERE DUP HERE = . CR -> 1 }TD 4 1388686 T= +Block 7085 T{ ALLOT.basic~2 T| HERE 10 ALLOT HERE SWAP - . CR -> }T 4 1506889 T= @@ -1395,13 +1357,13 @@ T| HERE 0 ALLOT HERE SWAP - . CR T{ ALLOT.negative~2 T| HERE -4 ALLOT HERE SWAP - . CR -> }T 4 1391569 T= -Block 7088 T{ ALLOT.large T| HERE 100 ALLOT HERE SWAP - . CR -> }T 5 13175195 T= T{ ALLOT.after_use T| ALIGN HERE 10 ALLOT 42 OVER ! @ . CR -> }T 4 1598184 T= +Block 7086 T{ ALLOT.empty_stack T| ALLOT ->ERR @@ -1411,13 +1373,13 @@ T| 42 , HERE 1 CELLS - @ . CR T{ ,.negative~2 T| -999 , HERE 1 CELLS - @ . CR -> }T 6 529078 T= -Block 7089 T{ ,.zero T| 0 , HERE 1 CELLS - @ . CR -> }T 3 47130 T= T{ ,.max_int T| 2147483647 , HERE 1 CELLS - @ . CR -> }T 12 4848918 T= +Block 7087 T{ ,.min_int T| -2147483648 , HERE 1 CELLS - @ . CR -> }T 13 5383556 T= @@ -1427,13 +1389,13 @@ T| 10 , 20 , HERE 1 CELLS - @ . HERE 1 CELLS - @ . CR T{ ,.empty_stack T| , ->ERR -Block 7090 T{ PAD.basic~2 T| PAD PAD = . CR -> }T 4 1388686 T= T{ PAD.different_from_here~2 T| PAD HERE = . CR -> }T 3 47130 T= +Block 7088 T{ PAD.stability T| PAD DUP PAD = . CR -> 1 }TD 4 1388686 T= @@ -1443,13 +1405,13 @@ T| CREATE test1 42 , test1 @ . CR T{ CREATE.empty_name T| CREATE ->ERR -Block 7091 T{ CREATE.redefine_shadows T| CREATE T 1 , CREATE T 2 , T @ . CR -> }T 3 49052 T= T{ CREATE.duplicate T| CREATE test2 CREATE test2 -> }T 0 0 T= +Block 7089 T{ CREATE.long_name T| CREATE abcdefghijklmnopqrstuvwxyz -> }T 0 0 T= @@ -1459,13 +1421,13 @@ T| CREATE temp1 FORGET temp1 T{ IMMEDIATE.basic T| : test3 42 ; IMMEDIATE test3 . CR -> }T 4 1598184 T= -Block 7092 T{ IMMEDIATE.already_immediate T| : test4 43 ; IMMEDIATE IMMEDIATE -> }T 0 0 T= T{ FIND.existing T| FIND DUP . CR -> 0 }TD +Block 7090 T{ FIND.user_word T| : test5 44 ; FIND test5 . CR -> 0 }TD @@ -1475,13 +1437,13 @@ T| FIND nonexistent . CR T{ FIND.empty T| FIND -> 0 }T 0 0 T= -Block 7093 T{ DEFINITIONS.basic T| FORTH DEFINITIONS -> }T 0 0 T= T{ DEFINITIONS.multiple T| FORTH DEFINITIONS DEFINITIONS -> }T 0 0 T= +Block 7091 T{ VOCABULARY.basic T| VOCABULARY TEST-VOC1 TEST-VOC1 DEFINITIONS -> }T 0 0 T= @@ -1492,13 +1454,13 @@ T{ VOCABULARY.word_isolation T| VOCABULARY ISOLATED ISOLATED DEFINITIONS : ISOWORD 99 ; FORTH T| DEFINITIONS -> }T 0 0 T= -Block 7094 T{ VOCABULARY.cross_vocab_access T| VOCABULARY V1 V1 DEFINITIONS : V1WORD 11 ; FORTH V1WORD . CR ->ERR T{ VOCABULARY.multiple_vocabs T| VOCABULARY VA VOCABULARY VB VOCABULARY VC VA DEFINITIONS -> }T 0 0 T= +Block 7092 T{ VOCABULARY.duplicate T| VOCABULARY TESTVOC VOCABULARY TESTVOC -> }T 0 0 T= @@ -1508,13 +1470,13 @@ T| VOCABULARY T{ FORTH.basic T| FORTH DEFINITIONS -> }T 0 0 T= -Block 7095 T{ FORTH.persistence T| FORTH : TEST1 42 ; TEST1 . CR -> }T 4 1598184 T= T{ FORTH.from_other T| VOCABULARY OTHER-VOC OTHER-VOC FORTH -> }T 0 0 T= +Block 7093 T{ DEFINITIONS.basic~2 T| FORTH DEFINITIONS -> }T 0 0 T= @@ -1524,13 +1486,13 @@ T| VOCABULARY TEST-VOC6 TEST-VOC6 DEFINITIONS T{ DEFINITIONS.word_creation T| VOCABULARY TEST-VOC7 TEST-VOC7 DEFINITIONS : TEST2 43 ; -> }T 0 0 T= -Block 7096 T{ CONTEXT.basic T| CONTEXT @ . CR -> 0 }TD T{ CONTEXT.initial T| FORTH CONTEXT @ . CR -> 0 }TD +Block 7094 T{ CURRENT.basic T| CURRENT @ . CR -> 0 }TD @@ -1540,13 +1502,13 @@ T| VOCABULARY TEST-VOC8 TEST-VOC8 DEFINITIONS CURRENT @ . CR T{ ABORT.basic T| ABORT -> }T 0 0 T= -Block 7097 T{ ABORT.with_data T| 1 2 3 ABORT DEPTH . CR -> }T 0 0 T= T{ ABORT.in_definition_runtime T| : BAD-WORD ABORT ; 123 BAD-WORD DEPTH . CR -> }T 0 0 T= +Block 7095 T{ :.basic T| : TEST1 42 ; TEST1 . CR -> }T 4 1598184 T= @@ -1556,13 +1518,13 @@ T| : T{ ;.alone T| ; ->ERR -Block 7098 T{ ;.immediate T| : TEST4 42 ; IMMEDIATE TEST4 . CR -> }T 4 1598184 T= T{ CONSTANT.basic T| 42 CONSTANT MEANING MEANING . CR -> }T 4 1598184 T= +Block 7096 T{ CONSTANT.zero T| 0 CONSTANT ZERO ZERO . CR -> }T 3 47130 T= @@ -1572,13 +1534,13 @@ T| -1 CONSTANT MINUS MINUS . CR T{ CONSTANT.empty_stack T| CONSTANT ->ERR -Block 7099 T{ VARIABLE.basic T| VARIABLE VAR1 42 VAR1 ! VAR1 @ . CR -> }T 4 1598184 T= T{ VARIABLE.multiple T| VARIABLE VAR2 VARIABLE VAR3 -> }T 0 0 T= +Block 7097 T{ VARIABLE.store_fetch T| VARIABLE VAR4 -99 VAR4 ! VAR4 @ . CR -> }T 5 9757879 T= @@ -1588,13 +1550,13 @@ T| CREATE OBJ1 T{ CREATE.with_data T| CREATE OBJ2 42 , OBJ2 @ . CR -> }T 4 1598184 T= -Block 7100 T{ CREATE.empty_name~2 T| CREATE ->ERR T{ DOES>.basic T| : CONST CREATE , DOES> @ ; 42 CONST MEANING MEANING . CR -> }T 4 1598184 T= +Block 7098 T{ DOES>.multiple T| : ARRAY CREATE DOES> SWAP CELLS + ; CREATE ARR 10 CELLS ALLOT -> }T 0 0 T= @@ -1604,13 +1566,13 @@ T| DOES> T{ [.basic T| : TEST5 [ 42 ] LITERAL ; TEST5 . CR -> }T 4 1598184 T= -Block 7101 T{ [.outside T| [ -> }T 0 0 T= T{ ].nested T| : TEST6 [ ] 42 ; TEST6 . CR -> }T 4 1598184 T= +Block 7099 T{ IF.true T| : TEST1 IF 42 ELSE 24 THEN ; -1 TEST1 . CR -> }T 4 1598184 T= @@ -1620,13 +1582,13 @@ T| : TEST2 IF 42 ELSE 24 THEN ; 0 TEST2 . CR T{ IF.nested T| : TEST3 IF IF 1 ELSE 2 THEN ELSE 3 THEN ; -1 -1 TEST3 . CR -> }T 3 48091 T= -Block 7102 T{ IF.no_else T| : TEST4 IF 42 THEN ; -1 TEST4 . CR -> }T 4 1598184 T= T{ IF.empty_stack T| IF ->ERR +Block 7100 T{ ELSE.alone T| ELSE ->ERR @@ -1636,13 +1598,13 @@ T| THEN T{ BEGIN.until T| : TEST5 0 BEGIN 1+ DUP 5 = UNTIL ; TEST5 . CR -> }T 3 51935 T= -Block 7103 T{ BEGIN.while T| : TEST6 0 BEGIN DUP 5 < WHILE 1+ REPEAT ; TEST6 . CR -> }T 3 51935 T= T{ BEGIN.alone T| BEGIN ->ERR +Block 7101 T{ UNTIL.basic T| : TEST8 BEGIN DUP 1- DUP 0= UNTIL ; 5 TEST8 . CR -> 5 4 3 2 1 }T 3 47130 T= @@ -1652,13 +1614,13 @@ T| : TEST9 BEGIN 1 UNTIL ; IMMEDIATE T{ UNTIL.no_begin T| UNTIL ->ERR -Block 7104 T{ WHILE.basic T| : TEST10 BEGIN DUP 5 < WHILE 1+ REPEAT ; 0 TEST10 . CR -> }T 3 51935 T= T{ WHILE.zero_times T| : TEST11 BEGIN DUP 0< WHILE 1+ REPEAT ; 0 TEST11 . CR -> }T 3 47130 T= +Block 7102 T{ WHILE.no_begin T| WHILE ->ERR @@ -1668,13 +1630,13 @@ T| : TEST12 BEGIN DUP 5 < WHILE 1+ REPEAT ; 0 TEST12 . CR T{ REPEAT.no_while T| REPEAT ->ERR -Block 7105 T{ DO.basic T| : TEST13 5 0 DO I . LOOP ; TEST13 CR -> }T 11 12528484 T= T{ DO.negative T| : TEST14 -1 -5 DO I . LOOP ; TEST14 CR -> }T 13 11696776 T= +Block 7103 T{ DO.empty T| : TEST15 0 0 DO LOOP ; -> }T 0 0 T= @@ -1684,13 +1646,13 @@ T| : TEST16 3 0 DO 3 0 DO J I + . LOOP LOOP ; TEST16 CR T{ LOOP.basic T| : TEST20 5 0 DO I . LOOP ; TEST20 CR -> }T 11 12528484 T= -Block 7106 T{ LOOP.no_do T| LOOP ->ERR T{ +LOOP.basic T| : TEST21 10 0 DO I . 2 +LOOP ; TEST21 CR -> }T 11 16410222 T= +Block 7104 T{ +LOOP.negative T| : TEST22 0 10 DO I . -1 +LOOP ; TEST22 CR -> }T 24 15411990 T= @@ -1700,13 +1662,13 @@ T| : TEST23 10 0 DO I . DUP +LOOP ; 3 TEST23 CR T{ I.basic T| : TI 4 0 DO I . LOOP CR ; TI -> }T 9 7781840 T= -Block 7107 T{ I.single T| : TI1 1 0 DO I . LOOP CR ; TI1 -> }T 3 47130 T= T{ I.offset T| : TI2 5 3 DO I . LOOP CR ; TI2 -> }T 5 14549425 T= +Block 7105 T{ I.in_expr T| : TI3 3 0 DO I 2 * . LOOP CR ; TI3 -> }T 7 9837120 T= @@ -1716,13 +1678,13 @@ T| : TJ 2 0 DO 3 0 DO J . LOOP LOOP CR ; TJ T{ J.paired T| : TJ2 2 0 DO 2 0 DO J I + . LOOP LOOP CR ; TJ2 -> }T 9 6857358 T= -Block 7108 T{ EXIT.early_return T| : TEX 5 EXIT 99 ; TEX . CR -> }T 3 51935 T= T{ EXIT.cond_exit_pos T| : TEX2 DUP 0> IF EXIT THEN NEGATE ; 3 TEX2 . CR -> }T 3 50013 T= +Block 7106 T{ EXIT.cond_exit_neg T| : TEX2 DUP 0> IF EXIT THEN NEGATE ; -4 TEX2 . CR -> }T 3 50974 T= @@ -1732,13 +1694,13 @@ T| : TEX3 1 2 EXIT 3 ; TEX3 . . CR T{ LEAVE.basic T| : TLV 0 5 0 DO I 3 = IF LEAVE THEN 1+ LOOP ; TLV . CR -> }T 3 50974 T= -Block 7109 T{ LEAVE.at_start T| : TLV2 0 5 0 DO LEAVE 1+ LOOP ; TLV2 . CR -> }T 3 48091 T= T{ LEAVE.qdloop T| : TLV3 0 5 0 ?DO I 2 = IF LEAVE THEN 1+ LOOP ; TLV3 . CR -> }T 3 50013 T= +Block 7107 T{ U*.small T| 3 4 U* . . CR -> }T 6 12751803 T= @@ -1748,13 +1710,13 @@ T| 0 5 U* . . CR T{ U*.full T| -1 -1 U* 2+ . 1 = . CR -> }T 6 12631678 T= -Block 7110 T{ U/MOD.small T| 7 0 2 U/MOD . . CR -> }T 5 14546542 T= T{ U/MOD.exact T| 12 0 4 U/MOD . . CR -> }T 5 14545581 T= +Block 7108 T{ U/MOD.double T| 0 1 2 U/MOD 2* . . CR -> }T 5 11775018 T= @@ -1764,13 +1726,13 @@ T| -1 -1 U* -1 U/MOD 1+ . . CR T{ U/MOD.carry T| -1 -2 -1 U/MOD 1+ . 2+ . CR -> }T 5 11775018 T= -Block 7111 T{ U/MOD.by_zero T| 1 0 0 U/MOD ->ERR T{ ?.basic T| VARIABLE Q1 42 Q1 ! Q1 ? CR -> }T 4 1598184 T= +Block 7109 T{ EXECUTE.found T| : E1 7 ; FIND E1 EXECUTE . CR -> }T 3 53857 T= @@ -1780,13 +1742,13 @@ T| 42 U. CR T{ U..unsigned T| -1 U. CR -> }T 22 3892143 T= -Block 7112 T{ CONVERT.digits T| : C1 0 0 BL WORD CONVERT DROP ; C1 123 . . CR -> }T 7 9448876 T= T{ '.found T| ' DUP 0= . CR -> }T 3 47130 T= +Block 7110 T{ .".in_definition T| : Q3 ." hi there" ; Q3 CR -> }T 9 13319717 T= @@ -1796,13 +1758,13 @@ T| : Q4 [ 5 ] LITERAL ; Q4 . CR T{ STATE.interpreting T| STATE @ . CR -> }T 3 47130 T= -Block 7113 T{ STATE.compiling T| : Q5 STATE @ 0= . ; IMMEDIATE : Q6 Q5 ; CR -> }T 3 47130 T= T{ COMPILE.basic T| : Q7 COMPILE DUP ; IMMEDIATE : Q8 Q7 ; 3 Q8 . . CR -> }T 5 14548464 T= +Block 7111 T{ [COMPILE].basic T| : Q9 [COMPILE] IF ; IMMEDIATE : Q10 Q9 1 ELSE 2 THEN ; 0 Q10 T| . -1 Q10 . CR @@ -1813,13 +1775,13 @@ T| 1 ( 2 ) 3 . . CR T{ BLK.terminal T| BLK @ . CR -> }T 3 47130 T= -Block 7114 T{ 79-STANDARD.present T| 79-STANDARD -> }T 0 0 T= T{ WORD.next_word T| : W1 BL WORD COUNT TYPE ; W1 HELLO -> }T 5 1515698 T= +Block 7112 T{ WORD.delimiter T| : W2 44 WORD COUNT TYPE ; W2 A B, -> }T 3 63523 T= @@ -1829,6 +1791,6 @@ T| : W3 BL WORD C@ . ; W3 ABC T{ WORD.skips_leading T| : W4 BL WORD COUNT TYPE ; W4 X -> }T 1 88 T= -Block 7115 +Block 7113 T-REPORT FORGET T# DECIMAL diff --git a/docs/v4.0.0/MESH.md b/docs/v4.0.0/MESH.md index 44b417bd..ec36bd20 100644 --- a/docs/v4.0.0/MESH.md +++ b/docs/v4.0.0/MESH.md @@ -213,38 +213,60 @@ outer nodes, and for each other unit the port toward that unit's centre. A different geometry is a different way of filling in the wiring table and these tables. Nothing else changes. -## 7a. Two neighbours writing to each other (open; awaits a ruling) +## 7a. Two neighbours writing to each other (ruled 2026-10-06) -The fault is in section 10, step 4. Three ways out, none built: +The fault is in section 10, step 4. **Ruled: a node writes only to a +neighbour that is already reading; a node keeps the messages it takes in +meanwhile, and one there is no room for is lost and counted.** -**A. A node writes only to a neighbour that is already reading.** The -engine gives a node two more things to fetch, as the F18's `io` register -does: which ports have a neighbour writing, and which have a neighbour -reading. A node with a message to send looks first. If the neighbour is -reading, it writes, and the write does not wait. If the neighbour is -writing to it instead, it takes that message in first, and deals with it, -and looks again. A node therefore holds messages waiting to go, in its own -memory, and what it does when that room is full has to be decided (the -message is lost and counted, as one with nowhere to go is now). Writes -still block, as ruled; a node simply does not start one it cannot finish. -This is how it is done on the chip. It is all in the nucleus but for the -two fetches. +**As built.** -**B. Passing on is not the node's programme's work.** `DECOMPOSITION.md` -6.1 says "the router in each node forwards packets". Read as a thing apart -from the programme -- logic beside the F18 on the FPGA, a part of the -fabric in the model -- it takes in and sends on whatever the programme is -doing, and holds what is for the programme until it reads. The programme's -own writes then never wait on a neighbour's programme. This is more -hardware per node and takes passing-on out of F18 code, and out of what a -capsule can change. +- *The engine.* Two more addresses follow a node's ports, as the F18's + `io` register would give them: which ports have a neighbour waiting to + write to this node, and which have one waiting to read from it, a bit to + a port. Fetching them waits for nothing and changes nothing + (`v4/src/node.c`; the fabric keeps them, `v4/src/fabric.c`). A device + that takes what is written to it shows as waiting to read. +- *Looking before writing* (`(GATE)`, `v4/capsule/core.v4`). Before a node + begins any message it takes in every message a neighbour is waiting to + write to it; then it writes if the neighbour it means to write to is + waiting to read; and if not, it looks again. Once a message is begun it + is written to its end: the neighbour that took its first word reads the + rest. +- *The messages waiting* (`(MQ)`). What a node takes in is kept in a ring + of 400 cells of its own memory -- for each message its seven words, the + port it came on, and its text -- and dealt with, oldest first, when the + node has nothing else to do. A node with none waiting is blocked reading + its ports, as before. One there is no room for is read to its end, let + go, and counted in `(LOST)`. -**C. No answer to a `SEND`.** It removes the case found and leaves the -fault: any two messages crossing still stop both nodes. Not offered as a -way out; listed so that it is seen to have been weighed. +**What had to be added to the ruling, and why.** As put to Captain Bob the +rule was "write only to a neighbour that is reading, and look again if it +is not". That is not enough: two neighbours each with a message for the +other would each look, see the other not reading, and look again, for +ever. No rule that treats both ends of a wire alike can get out of that. +So one end of every wire may write without waiting for a reader, and one +only: **the node with the lower number.** A node that waits to write is +then always waiting on a higher number, so no ring of nodes can all be +waiting on each other, and the highest of any that wait is not waiting to +write: it is looking, and takes in what is being written to it. -Recommended: A. It keeps every ruling as it stands, keeps a node an F18 -and nothing more, and what it adds to the engine the chip already has. +For this a node must know the number of the node on each of its ports. +Whoever wires it tells it, as it is told the ways: `NEIGHBOUR ( node port +-- )`, `v4/capsule/quit.v4`. A port it has not been told of -- a device's +-- is written to only when what is there is waiting to read. **Two nodes +wired together and not told of each other can still stop each other**, +each looking for ever; they execute, but nothing passes. Telling them is +part of wiring them. Put to Captain Bob after it was built, and ruled: +"1 is fine." + +**What it costs.** A node that is flooded loses messages, of every kind: +text for it, answers to text it sent, and messages it was only passing +on. In the test three nodes send each other 800 messages at once; 287 +arrive and 714 messages are let go (the count includes answers and text +that was to start a node sending). Six from each to each, at once, all +arrive. Nothing here makes a sender slow down or send again; that is +kernel-Hermes's work in v3 and is not decided for the mesh. ## 8. Storage @@ -344,8 +366,7 @@ Each is tested, committed and pushed before the next. input registers, not a message. A message not for this node, or not text, is let go: passing it on is step 4. 4. **Finding the way (section 7).** - **Built 2026-10-06; NOT done: one fault stands, below, and it needs a - ruling.** Each node has a table of up to 16 destinations and the port + **Built 2026-10-06.** Each node has a table of up to 16 destinations and the port toward each, and one port for everything else (`ROUTE ( node port -- )`, `DEFAULT-ROUTE ( port -- )`, `NO-ROUTES`; `(PORT-FOR)` in `v4/capsule/core.v4`). A message not for this node is written, whole, @@ -365,23 +386,39 @@ Each is tested, committed and pushed before the next. unchanged: `hosted-check` on three ISAs; bare metal `logs/20261006-115225` (amd64), `-115501` (aarch64), `-115849` (riscv64). - **THE FAULT: two neighbours that write to each other at once wait for - ever.** A write blocks until the neighbour reads (ruling, section 4.1), - and a node that is blocked writing is not reading. Passing a message on - is done here by the node's own programme, so a node cannot pass - anything on, or take anything in, while it waits to write. Found so: the - far node `SEND`s text to the middle one and then goes to send word of - how its own text ended, which goes by the middle one; the middle one has - done the text and goes to answer the far one. Each is writing the first - word of a message to the other. It is not peculiar to `SEND`: any two - messages going opposite ways on one wire at the same moment do it, and - so would a ring of nodes each writing to the next. The last check in - `test_host_mesh.c` shows it and is named KNOWN FAULT; it is there so the - fault is on record and is seen to go. - It is not a slip in the code. It is what blocking writes and a - programme that does one thing at a time give. Section 7 said nothing - about it and should have. What is to be done about it is not decided: - the choices are in section 7a. + **The fault found here, and put right 2026-10-06 (section 7a).** Two + neighbours that wrote to each other at once waited for ever: a write + blocks until the neighbour reads, and a node that is blocked writing is + not reading. Found when the far node `SEND`s text to the middle one and + then sends word of how its own text ended, which goes by the middle + one, while the middle one answers the far one. Ruled: a node writes only + to a neighbour that is reading, keeps what it takes in meanwhile, and + loses and counts what it has no room for. Built so, with one thing + added that the ruling needs (section 7a): of two neighbours the lower + number may wait to write, and `NEIGHBOUR` tells a node who is on each + port. `test_host_mesh.c`, 44 checks: the case that stopped the nodes + now passes; every node sending every other six messages at once, all + arrive; 800 at once, the nodes come to rest and every message either + arrived or was counted. `test_fabric.c`: a node sees who is waiting to + write to it and who to read from it, and looking disturbs neither. + The prompt and `EMIT` take no more of the data stack than they did. + All v4 tests at both widths and under ASan and UBSan; `hosted-check` on + three ISAs; bare metal, with lines typed at each prompt, + `logs/20261006-134817` (amd64), `-135044` (aarch64), `-135440` + (riscv64). + **Found on the way: POST was writing into the nucleus's own code.** On + v4 `HERE` is a cell address and `C!`, `FILL` and `CMOVE` take byte + addresses (D-1), so cases such as `65 HERE C!` wrote their bytes into + the nucleus at cell `HERE/4`, read them back from there, and passed. + `65 HERE C!` was watched changing cell 2223 during POST; the other + cases of the same form were not watched, and the boots committed + before this were not gone back to. It showed only when this step moved + other code onto that cell and POST stopped. Ruled: the twelve cases + are left out, marked OPEN (`v4/tools/post79_rules.py`, + `docs/v4.0.0/POST79.md` section 4), and how `HERE` and the byte words + are to agree is its own step. POST is 538 cases, not 550. `C!` and + `FILL` now have fewer cases; nothing stops any other text doing what + those cases did. 5. **Birth and Hera's requests (section 9); the unit of five.** 6. **Storage (section 8).** 7. **A second unit; scaling while running; sleep, wake and kill by diff --git a/docs/v4.0.0/POST79.md b/docs/v4.0.0/POST79.md index e8bce0f4..5eae5314 100644 --- a/docs/v4.0.0/POST79.md +++ b/docs/v4.0.0/POST79.md @@ -3,7 +3,7 @@ Written by `v4/tools/mkpost.py` from `v4/tools/post79_rules.py`; do not edit. Design: `NUCLEUS.md` section 6. The capsule is `capsules/v4/post79.4th`. -POST runs 550 cases. 443 are v3's, with what the hosted v3 binary did as the +POST runs 538 cases. 431 are v3's, with what the hosted v3 binary did as the expected result. This file lists every other case, and why. ## 1. Rewritten for FORTH-79 (59) @@ -60,11 +60,11 @@ standard's and not v3's. | `CMOVE.basic` | `HERE S" Test" DUP >R HERE 10 + SWAP CMOVE CR` | stack `325`, printed `\n` | `65 PAD C! 66 PAD 1+ C! PAD PAD 10 + 2 CMOVE PAD 10 + C@ . PAD 11 + C@ . CR` | stack `empty`, prints `65 66 \n` | v3's address unit is a byte; a v4 address unit is a cell (D-1); and the v3 case leaves a value on the return stack | | `TYPE.basic_string` | `HERE S" Hello" DUP >R HERE SWAP CMOVE HERE R> TYPE CR` | stack `400`, printed `Hello\n` | `72 PAD C! 105 PAD 1+ C! PAD 2 TYPE CR` | stack `empty`, prints `Hi\n` | v3's address unit is a byte; a v4 address unit is a cell (D-1); and FORTH-79: >R R> R@ are for use inside a definition; v4 refuses them at the prompt | | `TYPE.numbers` | `HERE S" 12345" DUP >R HERE SWAP CMOVE HERE R> TYPE CR` | stack `406`, printed `12345\n` | `49 PAD C! 50 PAD 1+ C! PAD 2 TYPE CR` | stack `empty`, prints `12\n` | v3's address unit is a byte; a v4 address unit is a cell (D-1); and FORTH-79: >R R> R@ are for use inside a definition; v4 refuses them at the prompt | -| `>IN.initial` | `>IN @ . CR` | stack `empty`, printed `852597488 \n` | unchanged | stack `empty`, prints `9 \n` | FORTH-79: >IN is the offset into the input, 9 here; v3 prints an address | +| `>IN.initial` | `>IN @ . CR` | stack `empty`, printed `1009040112 \n` | unchanged | stack `empty`, prints `9 \n` | FORTH-79: >IN is the offset into the input, 9 here; v3 prints an address | | `UPDATE.no_block` | `0 SCR ! UPDATE` | error | unchanged | stack `empty`, prints nothing | v3 raises an error FORTH-79 does not ask for | | `SCR.after_load` | `1 LOAD SCR @ . CR` | stack `empty`, printed `` | unchanged | stack `empty`, prints `1 \n` | v3 prints nothing after 1 LOAD; SCR is still 1 from the LIST before | -| `FIND.existing` | `FIND DUP . CR` | stack `empty`, printed `852596848 \n` | unchanged | 0 value(s) left; output not compared | FORTH-79: FIND takes the next word, DUP here, and leaves its address, which is printed | -| `FIND.user_word` | `: test5 44 ; FIND test5 . CR` | stack `empty`, printed `852923888 \n` | unchanged | 0 value(s) left; output not compared | the address FIND leaves is printed | +| `FIND.existing` | `FIND DUP . CR` | stack `empty`, printed `1009039472 \n` | unchanged | 0 value(s) left; output not compared | FORTH-79: FIND takes the next word, DUP here, and leaves its address, which is printed | +| `FIND.user_word` | `: test5 44 ; FIND test5 . CR` | stack `empty`, printed `1009366512 \n` | unchanged | 0 value(s) left; output not compared | the address FIND leaves is printed | | `FIND.empty` | `FIND` | error | unchanged | stack `0`, prints nothing | FORTH-79: FIND leaves 0 when there is no word; v3 raises an error | | `VOCABULARY.cross_vocab_access` | `VOCABULARY V1 V1 DEFINITIONS : V1WORD 11 ; FORTH V1WORD . CR` | stack `empty`, printed `11 \n` | unchanged | an error | FORTH-79: a word defined in another vocabulary is not found from FORTH; v3 finds it | | `VOCABULARY.duplicate` | `VOCABULARY TESTVOC VOCABULARY TESTVOC` | error | unchanged | stack `empty`, prints nothing | v3 raises an error FORTH-79 does not ask for: defining a name again | @@ -139,7 +139,7 @@ standard's; no v3 run stands behind them. | `WORD.count` | `: W3 BL WORD C@ . ; W3 ABC` | stack `empty`, prints `3 ` | | `WORD.skips_leading` | `: W4 BL WORD COUNT TYPE ; W4 X` | stack `empty`, prints `X` | -## 4. v3 cases left out (27) +## 4. v3 cases left out (39) | Case | Why | |---|---| @@ -155,7 +155,19 @@ standard's; no v3 run stands behind them. | `].basic` | v3 did not get through it in one session | | `ELSE.double` | v3 did not get through it in one session | | `THEN.extra` | v3 did not get through it in one session | +| `C!.basic` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | +| `C!.zero` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | +| `C!.high_byte` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | +| `C!.truncation` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | +| `C@.after_cstore` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | +| `C@.zero_byte` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | +| `FILL.basic` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | +| `FILL.zero_byte` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | +| `COUNT.string_bounds` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | +| `CMOVE.overlap` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | | `CMOVE.bounds` | moves 1000 bytes over the system; v3 refuses by a bounds rule FORTH-79 has not got | +| `TYPE.single_char` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | +| `COUNT.zero_addr_plus_one` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 | | `WORD.empty_input` | v3's WORD does not take the next word from the input, as FORTH-79's does; see the WORD cases written by hand | | `WORD.space_delim` | v3's WORD does not take the next word from the input, as FORTH-79's does; see the WORD cases written by hand | | `WORD.newline_delim` | v3's WORD does not take the next word from the input, as FORTH-79's does; see the WORD cases written by hand | diff --git a/logs/20261006-134817/amd64/qemu-amd64-20261006-134817.log b/logs/20261006-134817/amd64/qemu-amd64-20261006-134817.log new file mode 100644 index 00000000..cf3c9274 --- /dev/null +++ b/logs/20261006-134817/amd64/qemu-amd64-20261006-134817.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 13:47:51 + +UEFI BootServices: EXITED + +=== StarKernel Boot Information === +Memory map entries: 131 +Total memory: 1023 MB +Usable memory: 968 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 249251 + Free pages : 247728 + 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=2101165142 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=1007014520, tick_hz=100, initial_count=10070145 +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=0xce1adfd156a70639 capsule_hash=0xce1adfd156a70639 words=306 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x24314ed49017b4ec +V4: capsule v4:post79.4th signature: missing (unsigned) +PARITY:V4_POST tests=538 pass=538 fail=0 +PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0x4bf51f6674743728 +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-135044/aarch64/qemu-aarch64-20261006-135044.log b/logs/20261006-135044/aarch64/qemu-aarch64-20261006-135044.log new file mode 100644 index 00000000..be3e2cc2 --- /dev/null +++ b/logs/20261006-135044/aarch64/qemu-aarch64-20261006-135044.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 13:50:19 + +UEFI BootServices: EXITED + +=== StarKernel Boot Information === +Memory map entries: 108 +Total memory: 4093 MB +Usable memory: 4056 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 1039865 + Free pages : 1038454 + 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=0xce1adfd156a70639 capsule_hash=0xce1adfd156a70639 words=306 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x24314ed49017b4ec +V4: capsule v4:post79.4th signature: missing (unsigned) +PARITY:V4_POST tests=538 pass=538 fail=0 +PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0x4bf51f6674743728 +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-135440/riscv64/qemu-riscv64-20261006-135440.log b/logs/20261006-135440/riscv64/qemu-riscv64-20261006-135440.log new file mode 100644 index 00000000..878a3fd6 --- /dev/null +++ b/logs/20261006-135440/riscv64/qemu-riscv64-20261006-135440.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 = 0x00000000bdea78f8 +riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a) + + + _____ _ _ __ _ + / ____| | | |/ / | | + | (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| | + \___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ | + ____) | || (_| | | | . \ __/ | | | | | __/ | + |_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_| + +LithosAnanke v2.1.0 +Architecture: riscv64 +Build: Oct 6 2026 13:54:02 + +UEFI BootServices: EXITED + +=== StarKernel Boot Information === +Memory map entries: 100 +Total memory: 1020 MB +Usable memory: 977 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 250782 + Free pages : 250130 + 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=0xce1adfd156a70639 capsule_hash=0xce1adfd156a70639 words=306 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x24314ed49017b4ec +V4: capsule v4:post79.4th signature: missing (unsigned) +PARITY:V4_POST tests=538 pass=538 fail=0 +PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0x4bf51f6674743728 +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/README.md b/v4/README.md index 2d143acf..23e16d5c 100644 --- a/v4/README.md +++ b/v4/README.md @@ -61,7 +61,7 @@ load `v4:forth79.4th`, pass POST and reach `ok>`, with the same lines: ``` PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=... capsule_hash=0x97a655081b2af559 words=298 PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=... dict_hash=... -PARITY:V4_POST tests=550 pass=550 fail=0 +PARITY:V4_POST tests=538 pass=538 fail=0 PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=... dict_hash=... PARITY:OK POST: PASSED @@ -115,11 +115,13 @@ steps 8 and 9. **Finding the way** (`MESH.md` step 4) is built: a node has a table of which port leads toward which node (`ROUTE`, `DEFAULT-ROUTE`, `NO-ROUTES`), passes on a message that is not for it, and `SEND`s text to another node; -`v4/tests/test_host_mesh.c` runs three StarForth nodes in a row. **It has -a fault that stands:** two neighbours that write to each other at the same -moment wait for ever, because a write blocks until the neighbour reads and -a node that is writing is not reading. The test shows it, named KNOWN -FAULT. The ways out are in `MESH.md` section 7a and none is chosen yet. +`v4/tests/test_host_mesh.c` runs three StarForth nodes in a row. A node +looks before it begins a message (`MESH.md` section 7a): it takes in what +its neighbours are waiting to write to it, keeps those messages until it +has nothing else to do, and writes only to a neighbour that is waiting to +read -- or, of two neighbours, the one with the lower number may wait. +`NEIGHBOUR ( node port -- )` tells a node who is on each port. A node with +no room for one more message lets it go and counts it in `(LOST)`. **This is still the lone node.** On bare metal `kernel_main.c` starts it before the fleet tables, beside the kernel's own system and not in the VM's @@ -133,7 +135,7 @@ a time with POST after each, is the next step (`NUCLEUS.md` section 8). ## POST -`capsules/v4/post79.4th`: 550 cases in blocks 7000 up, covering 126 of the +`capsules/v4/post79.4th`: 538 cases in blocks 7000 up, covering 126 of the 130 words of the FORTH-79 Required Word Set. 443 are v3's cases with what the hosted v3 binary did as the expected result. The other 107, and the 27 v3 cases left out, are listed with reasons in `docs/v4.0.0/POST79.md`. diff --git a/v4/capsule/core.v4 b/v4/capsule/core.v4 index ab369018..48ca494e 100644 --- a/v4/capsule/core.v4 +++ b/v4/capsule/core.v4 @@ -128,6 +128,98 @@ header CMOVE NONE: drop drop (ROUTE-DEFAULT) a! @ ; HIT: drop drop @+ pop drop ; +\ LOOKING BEFORE WRITING (MESH.md section 7a). A write waits until the +\ neighbour reads, and a node that is waiting to write reads nothing: two +\ neighbours that each began to write to the other would wait for ever. So +\ a node does not begin a message until it has looked, (GATE): +\ - while any neighbour is waiting to write to it, it takes that message +\ in and keeps it with the messages waiting, to deal with when it has +\ nothing else to do; +\ - then, if the neighbour it means to write to is waiting to read, it +\ writes; +\ - and if that neighbour is not, it writes all the same when the +\ neighbour's number is the higher of the two, and otherwise looks +\ again. Of two neighbours one only may wait to write to the other, and +\ it is the lower: so those that wait are waiting on ever higher +\ numbers, and the highest of them is not waiting to write; it is +\ looking, and takes in what is being written to it. +\ (WRITERS) and (READERS) are the node's two looks at its neighbours, a bit +\ for each port. (NEAR) is the number of the node on each port, told by +\ whoever wires it (NEIGHBOUR, quit.v4); a port not told of -- a device's +\ -- is written to only when what is there is waiting to read. +\ +\ THE MESSAGES WAITING are kept in (MQ) .. (MQ-END), one after another, +\ going round: for each its seven words, the port it came on, and its text. +\ (MQ-HEAD) is where the oldest begins, (MQ-TAIL) where the next will go, +\ (MQ#) how many cells are taken. One that there is no room for is read to +\ its end and let go, and (LOST) counts it. + +\ ( mask index -- bit ) +: (BIT) + if Z -1 + FOR 2/ NEXT 1 and ; + Z: drop 1 and ; + +\ ( mask -- index ) the lowest port in it; the mask is not zero +: (LOW) + 0 push + L: dup 1 and if UP drop drop pop ; + UP: drop 2/ pop 1 + push jump L + +\ ( w -- ) one more cell of the messages waiting. B is kept. +: (MQ!) + (MQ-TAIL) a! @ a! !+ + a (MQ-END) xor if WRAP drop a jump SET + WRAP: drop (MQ) + SET: (MQ-TAIL) a! ! + (MQ#) a! @ 1 + ! ; + +\ ( -- w ) the oldest cell of the messages waiting, which goes +: (MQ@) + (MQ-HEAD) a! @ a! @+ + a (MQ-END) xor if WRAP drop a jump SET + WRAP: drop (MQ) + SET: (MQ-HEAD) a! ! + (MQ#) a! @ -1 + ! ; + +\ ( port to -- ) take in a message. B is at the port, by its address, +\ and `to` is the message's first word, already read from it. A length +\ below zero, or above what a message carries, is kept as -1 with no text: +\ a longer one is read to its end first, so that what follows it is not +\ taken for a message. +: (TAKE) + (MQ-HDR) a! !+ 5 FOR @b !+ UNEXT ! \ its seven words, and the port + (MQ-HDR)+6 a! @ -if SIZED + drop -1 (MQ-HDR)+6 a! ! 0 jump COUNTED + SIZED: dup -1025 + -if LONG drop 3 + 2/ 2/ jump COUNTED + LONG: drop 3 + 2/ 2/ -1 + FOR @b drop UNEXT -1 (MQ-HDR)+6 a! ! 0 + COUNTED: \ ( n ) how many words of text follow + dup (MQ#) a! @ + -MQ-ROOM + -if FULL + drop push + (MQ-HDR) a! @ (MQ!) (MQ-HDR)+1 a! @ (MQ!) (MQ-HDR)+2 a! @ (MQ!) (MQ-HDR)+3 a! @ (MQ!) + (MQ-HDR)+4 a! @ (MQ!) (MQ-HDR)+5 a! @ (MQ!) (MQ-HDR)+6 a! @ (MQ!) (MQ-HDR)+7 a! @ (MQ!) + pop if NOTEXT -1 + FOR @b (MQ!) NEXT ; + NOTEXT: drop ; + FULL: drop if GONE -1 + FOR @b drop UNEXT jump LOSE + GONE: drop + LOSE: (LOST) a! @ 1 + ! ; + +\ ( port -- ) wait until a message may be begun on the port, by its +\ address, taking in whatever is being written to this node meanwhile; and +\ leave B at the port. The port is kept in (GATE-PORT), not on the stack: +\ this is run with the stack as full as EMIT may be. +: (GATE) + (GATE-PORT) a! ! + L: (WRITERS) b! @b if QUIET + (LOW) (PORT) + dup b! @b (TAKE) jump L + QUIET: drop + (GATE-PORT) a! @ (PORT) - (NEAR) + a! @ if ASK + (ME) a! @ - -if GO jump ASK \ its number less this node's + GO: drop (GATE-PORT) a! @ b! ; \ the neighbour's is the higher + ASK: drop + (READERS) b! @b (GATE-PORT) a! @ (PORT) - (BIT) if NOTYET + drop (GATE-PORT) a! @ b! ; \ it is waiting to read + NOTYET: drop jump L + \ ( type -- ) words 1 to 5 of a message this node sends: from, type, and \ the three that are carried and not used yet. Its caller has put B at the \ port and sent word 0, whom it is to, and sends the length and the text. @@ -142,7 +234,7 @@ header CMOVE \ lowest. quit.v4 sets those two when text for this node arrives. : (FLUSH-OUT) (OUT^) a! @ (OUT) xor if NONE drop - (PRINT-PORT) a! @ b! (PRINT-TO) a! @ !b + (PRINT-PORT) a! @ (GATE) (PRINT-TO) a! @ !b 2 (HDR) (OUT^) a! @ (OUT) - dup !b 3 + 2/ 2/ -1 + (OUT) a! diff --git a/v4/capsule/quit.v4 b/v4/capsule/quit.v4 index c835f0b8..dc8510ee 100644 --- a/v4/capsule/quit.v4 +++ b/v4/capsule/quit.v4 @@ -65,7 +65,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro : (FINISH) ( s -- ) (LINE-STATUS) b! dup !b (FLUSH-OUT) - (DONE-PORT) a! @ b! (MSG)+1 a! @ !b \ to whoever sent the text + (DONE-PORT) a! @ (GATE) (MSG)+1 a! @ !b \ to whoever sent the text 3 (HDR) 4 !b !b \ a message of type 3, four characters long: how it ended jump (IDLE) @@ -78,29 +78,30 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro OK: drop 1 jump (FINISH) GO: drop 0 jump (FINISH) -\ ( -- ) WAITING. A node with nothing to do is blocked reading its ports -\ (docs/v4.0.0/MESH.md 4.1): it executes nothing until a neighbour writes. -\ What arrives is a message (core.v4). Its first word is taken from any -\ port and the rest from the port that came on. Its text goes into TIB, -\ which holds 1024 characters, the most a message carries; a longer one is -\ read to its end, so that what follows it is not taken for a message, and -\ is error 12. A message of type 1 for this node is text to interpret: it -\ is done as a line was, and (FINISH) reports how it ended. A message for -\ another node is passed on, (PASS-ON). +\ ( -- ) WAITING. A node with nothing to do deals with the oldest of the +\ messages waiting (core.v4). With none waiting it is blocked reading its +\ ports (docs/v4.0.0/MESH.md 4.1): it executes nothing until a neighbour +\ writes, and what arrives is taken in as any message is, its first word +\ from any port and the rest from the port that came on. The message's +\ text goes into TIB, which holds 1024 characters, the most a message +\ carries; one that came with a length no message has is error 12. A +\ message of type 1 for this node is text to interpret: it is done as a +\ line was, and (FINISH) reports how it ended. A message for another node +\ is passed on, (PASS-ON). : (IDLE) - (PORT)+8 b! @b (MSG) a! !+ \ to, from any port - (PORT)+9 b! @b (PORT) + dup b! \ B: the port it came on - 5 FOR @b !+ UNEXT \ from, type, heat and TTL, ACL tag, sequence, length - (REPLY) a! ! - (MSG)+6 a! @ -if SIZED jump BAD \ a length below zero: no telling what follows - SIZED: dup -1025 + -if LONG drop jump FITS - LONG: drop 3 + 2/ 2/ -1 + FOR @b drop UNEXT 0 \ too long: read it and let it go - BAD: drop 0 (MSG)+6 a! ! 0 SPAN a! ! NODE-ERROR b! 12 !b - FITS: - 3 + 2/ 2/ if EMPTY - -1 + TIB 2/ 2/ a! FOR @b !+ UNEXT - jump READ - EMPTY: drop + L: (MQ#) a! @ if WAIT drop jump HAVE + WAIT: drop (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! \ ( to port ) B: the port it came on + SWAP (TAKE) jump L + HAVE: + (MQ@) (MSG) a! ! (MQ@) (MSG)+1 a! ! (MQ@) (MSG)+2 a! ! (MQ@) (MSG)+3 a! ! + (MQ@) (MSG)+4 a! ! (MQ@) (MSG)+5 a! ! (MQ@) (MSG)+6 a! ! (MQ@) (REPLY) a! ! + (MSG)+6 a! @ -if SIZED + drop 0 (MSG)+6 a! ! 0 SPAN a! ! NODE-ERROR b! 12 !b \ no telling what it was + SIZED: drop + TIB 2/ 2/ (MSG)+6 a! @ 3 + 2/ 2/ if EMPTY + -1 + FOR (MQ@) over a! ! 1 + NEXT + drop jump READ + EMPTY: drop drop READ: 0 (MSG)+6 a! @ TIB + C! \ a zero after the text (MSG)+6 a! @ SPAN a! ! @@ -124,7 +125,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro \ known it is let go, and (LOST) counts it. : (PASS-ON) (MSG) a! @ (PORT-FOR) if NOWAY - b! (MSG) a! 6 FOR @+ !b UNEXT \ its seven words + (GATE) (MSG) a! 6 FOR @+ !b UNEXT \ its seven words (MSG)+6 a! @ 3 + 2/ 2/ if SENT -1 + TIB 2/ 2/ a! FOR @+ !b UNEXT \ and its text jump (IDLE) @@ -156,6 +157,16 @@ header ROUTE (ROUTE#) a! @ 1 + ! ; FULL: drop drop drop NODE-ERROR b! 12 !b ; +\ ( node port -- ) the node on the other end of the port, by its number; +\ 0: none, or a device. Whoever wires the node tells it, and tells it again +\ when the wiring changes. It is what lets one of two neighbours, and one +\ only, wait to write to the other (core.v4, (GATE)). +header NEIGHBOUR +: NEIGHBOUR + dup -if POS jump BAD + POS: -PORTS + -if BAD drop (NEAR) + a! ! ; + BAD: drop drop drop NODE-ERROR b! 12 !b ; + \ ( port -- ) the port, by its number, leads toward every node not told of header DEFAULT-ROUTE : DEFAULT-ROUTE (PORT) + (ROUTE-DEFAULT) a! ! ; @@ -167,12 +178,11 @@ header NO-ROUTES \ ( baddr u node -- ) send the node u characters of text to interpret. It \ is on its way when SEND returns; what it prints there goes to that node's \ console. With no way to the node it is error 12. The write waits for the -\ neighbour to read: a neighbour that is itself waiting to write to this -\ node will wait for ever, and so will this one. +\ neighbour to read, and is not begun until it may be (core.v4, (GATE)). header SEND : SEND dup (PORT-FOR) if NOWAY - b! !b \ to + (GATE) !b \ to 1 (HDR) dup !b \ length 3 + 2/ 2/ if NONE -1 + diff --git a/v4/include/v4/node.h b/v4/include/v4/node.h index 33f4c2e7..c9e62035 100644 --- a/v4/include/v4/node.h +++ b/v4/include/v4/node.h @@ -109,6 +109,8 @@ typedef struct { v4_cell given_value; unsigned given_port; unsigned last_from; /* the port the last read from "any" took its word from */ + unsigned writers; /* bit k: the neighbour on port k is blocked writing to this node */ + unsigned readers; /* bit k: the neighbour on port k is blocked reading from this node */ /* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */ v4_cell dstack_reg; /* its word address, or -1 */ @@ -181,6 +183,13 @@ void v4_node_console_attach(v4_node *n, v4_cell addr); * base + V4_PORTS "any port": a read here takes from whichever port * has a neighbour writing * base + V4_PORTS + 1 which port the last read from "any" came from + * base + V4_PORTS + 2 which ports have a neighbour waiting to write to this + * node: bit k for port k + * base + V4_PORTS + 3 which ports have a neighbour waiting to read from it + * The last two are how a node looks before it writes (MESH.md 7a), as the + * F18's io register lets it. They are what whoever connects the ports last + * said, v4_node_port_status; fetching them waits for nothing and changes + * nothing. * (read only) * None of them is memory while the ports are attached. * @@ -213,6 +222,11 @@ void v4_node_port_served(v4_node *n); * port or "any", is unblocked and will fetch it. */ void v4_node_port_give(v4_node *n, unsigned port, v4_cell value); +/* What is on the other end of the node's ports now: bit k of `writers` if + * the neighbour on port k is blocked writing to this node, bit k of + * `readers` if it is blocked reading from it and would take a word. */ +void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers); + /* Which port `addr` is: 0 .. V4_PORTS - 1, V4_PORT_ANY, V4_PORTS + 1 for the * "which port" address, or -1 if it is none of them. */ int v4_node_port_index(const v4_node *n, v4_cell addr); diff --git a/v4/src/fabric.c b/v4/src/fabric.c index 18168be5..8eb8423b 100644 --- a/v4/src/fabric.c +++ b/v4/src/fabric.c @@ -155,10 +155,34 @@ static unsigned hand_over_read(v4_fabric *f, unsigned a) return 0; } +/* What node `a` can see of its neighbours before it executes: who is + * blocked writing to it, and who would take a word from it. A device is + * always ready to be written to; what it has to give, the node finds when + * it reads. */ +static void look_about(v4_fabric *f, unsigned a) +{ + unsigned k, writers = 0, readers = 0; + + for (k = 0; k < V4_PORTS; k++) { + const v4_wire *w = &f->place[a].wire[k]; + if (w->kind == V4_WIRE_DEVICE) { + if (w->device->take) readers |= 1u << k; + } else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) { + const v4_node *o = &f->place[w->node].node->n; + if (o->asking && o->ask_port == w->port) writers |= 1u << k; + if (o->reading && !o->given && (o->read_port == w->port || o->read_port == V4_PORT_ANY)) readers |= 1u << k; + } + } + v4_node_port_status(&f->place[a].node->n, writers, readers); +} + unsigned v4_fabric_step(v4_fabric *f) { unsigned i, done = 0; + for (i = 0; i < f->capacity; i++) + if (awake(f, i)) look_about(f, i); + for (i = 0; i < f->capacity; i++) if (awake(f, i)) done += v4_exec_step_word(&f->place[i].node->n, &f->place[i].node->es, &f->place[i].node->h); diff --git a/v4/src/node.c b/v4/src/node.c index 211a54a8..1fb7925e 100644 --- a/v4/src/node.c +++ b/v4/src/node.c @@ -159,6 +159,8 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr) { int port = v4_node_port_index(n, addr); if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from; + if (port == (int)V4_PORTS + 2) return (v4_cell)n->writers; + if (port == (int)V4_PORTS + 3) return (v4_cell)n->readers; if (port >= 0) { /* the word its neighbour wrote; v4_node_read_ready has said it is there */ if (!n->given) return 0; @@ -214,18 +216,26 @@ void v4_node_port_attach(v4_node *n, v4_cell base) n->given_value = 0; n->given_port = 0; n->last_from = 0; + n->writers = 0; + n->readers = 0; +} + +void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers) +{ + n->writers = writers; + n->readers = readers; } int v4_node_port_index(const v4_node *n, v4_cell addr) { - if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORTS + 1) return -1; + if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORTS + 3) return -1; return (int)(addr - n->port); } int v4_node_read_ready(v4_node *n, v4_cell addr) { int port = v4_node_port_index(n, addr); - if (port < 0 || port == (int)V4_PORTS + 1) return 1; + if (port < 0 || port > (int)V4_PORT_ANY) return 1; if (n->given && (port == (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1; n->reading = 1; n->read_port = (unsigned)port; diff --git a/v4/system/boot.c b/v4/system/boot.c index a7f89e70..fe8c9e24 100644 --- a/v4/system/boot.c +++ b/v4/system/boot.c @@ -365,6 +365,10 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks) say(b, "V4: the nucleus is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n"); return 0; } + /* what is on its two ports, the kernel and the console, takes a word + * when it is written: the node need never wait before writing to them + * (node.h, v4_node_port_status) */ + v4_node_port_status(b->n, 0, 1u << KERNEL_PORT | 1u << CONSOLE_PORT); if (!load_nucleus(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; } /* the kernel's words, before any capsule that might use one */ diff --git a/v4/tests/host_map.h b/v4/tests/host_map.h index 1629c02c..e695a344 100644 --- a/v4/tests/host_map.h +++ b/v4/tests/host_map.h @@ -74,8 +74,18 @@ #define ROUTE_DEFAULT (BUF0_W - 18) /* the port address for a node not in the table, or 0: there is none */ #define LOST (BUF0_W - 19) /* how many messages have been let go for want of a way */ #define PRINT_TO (BUF0_W - 20) /* where what the text being served prints is to go: the node, */ -#define PRINT_PORT (BUF0_W - 21) /* and the port address that leads there */ -#define DONE_PORT (BUF0_W - 22) /* the port address that leads back to whoever sent the text being served */ +#define WRITERS (PORT + (v4_cell)V4_PORTS + 2) /* which ports have a neighbour waiting to write to this node (node.h) */ +#define READERS (PORT + (v4_cell)V4_PORTS + 3) /* and which have one waiting to read from it */ +#define PRINT_PORT (BUF0_W - 33) /* and the port address that leads there */ +#define DONE_PORT (BUF0_W - 34) /* the port address that leads back to whoever sent the text being served */ +#define MQ_HEAD (BUF0_W - 35) /* the messages waiting to be dealt with: where the oldest begins, */ +#define MQ_TAIL (BUF0_W - 36) /* where the next will go, */ +#define MQ_COUNT (BUF0_W - 37) /* and how many cells they take */ +#define GATE_PORT (BUF0_W - 38) /* the port address a message is about to be begun on */ +#define NEAR (BUF0_W - 64) /* for each port, the number of the node on the other end; 0: not told */ +#define MQ_HDR (BUF0_W - 56) /* a message being taken in: its seven words and the port it came on */ +#define MQ_W (OUT_W - 400) /* the messages waiting: each is its seven words, the port it came on, its text */ +#define MQ_CELLS 400 #define ROUTES (BUF0_W - 96) /* the table of ways: 16 entries of a node and the port address that leads to it */ #define ROUTE_MAX 16 #define ME (BUF0_W - 13) /* this node's number: a message is for it when its first word is this */ @@ -90,6 +100,10 @@ #define BOOT_CELLS (BVARS + 14) /* (BOOT): DP and LATEST as the loader left them, 2 cells */ #define BUF0_W (BVARS - 2 * 256) /* the two block buffers, 256 cells each */ #define BUF1_W (BUF0_W + 256) +/* the ports' ten addresses and the two that follow them end below BUF0_W - 20 */ +typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1]; +/* Nothing printed and no message waiting: a node's variables for them, as at switch-on. */ +#define HOST_MESSAGES_EMPTY(n) do { (n)->mem[OUT_PTR] = OUT_W; (n)->mem[MQ_HEAD] = MQ_W; (n)->mem[MQ_TAIL] = MQ_W; (n)->mem[MQ_COUNT] = 0; } while (0) #define QVARS (BUF0_W - 4) /* (Q): quit.v4, system.v4 and blocks.v4, 4 cells */ #define WBUF (WBUF_W * 4) #define TIB (TIB_W * 4) @@ -103,6 +117,8 @@ #ifndef DICT_END_W #define DICT_END_W ((v4_cell)14336) #endif +/* the messages waiting are above the dictionary */ +typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1]; /* How many slots, from slot 0, a branch may sit in on a node this size: those * whose address field reaches every word of it. */ @@ -123,7 +139,7 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned unsigned i; v4_node_reset(n); - n->mem[OUT_PTR] = OUT_W; /* nothing has been printed: what is, is kept from here (core.v4) */ + HOST_MESSAGES_EMPTY(n); /* nothing has been printed and no message waits (core.v4) */ v4_text_begin(tx, n, 16); v4_text_constant(tx, "N-1", V4_CELL_BITS - 1); v4_text_constant(tx, "NODE-ERROR", NODE_ERROR); @@ -173,6 +189,18 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned v4_text_constant(tx, "(PRINT-PORT)", PRINT_PORT); v4_text_constant(tx, "(DONE-PORT)", DONE_PORT); v4_text_constant(tx, "(ROUTES)", ROUTES); + v4_text_constant(tx, "(WRITERS)", WRITERS); + v4_text_constant(tx, "(READERS)", READERS); + v4_text_constant(tx, "-PORTS", -(v4_cell)V4_PORTS); + v4_text_constant(tx, "(NEAR)", NEAR); + v4_text_constant(tx, "(GATE-PORT)", GATE_PORT); + v4_text_constant(tx, "(MQ)", MQ_W); + v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS); + v4_text_constant(tx, "-MQ-ROOM", -(v4_cell)(MQ_CELLS - 7)); /* cells taken + a text's words + this is below zero while the message fits */ + v4_text_constant(tx, "(MQ-HEAD)", MQ_HEAD); + v4_text_constant(tx, "(MQ-TAIL)", MQ_TAIL); + v4_text_constant(tx, "(MQ#)", MQ_COUNT); + v4_text_constant(tx, "(MQ-HDR)", MQ_HDR); v4_text_constant(tx, "(OUT^)", OUT_PTR); v4_text_constant(tx, "(REPLY)", REPLY); v4_text_constant(tx, "(MSG)", MSG); diff --git a/v4/tests/test_fabric.c b/v4/tests/test_fabric.c index d3e7790c..39a04672 100644 --- a/v4/tests/test_fabric.c +++ b/v4/tests/test_fabric.c @@ -331,6 +331,29 @@ int main(void) "a port that is not there, a node that is not there, a port to itself: refused"); CHECK(v4_fabric_wire(&f, a, 1, a, 2), "but one port of a node may be wired to another of the same node"); + /* ---- a node looks at its neighbours before it writes (MESH.md 7a) ---- */ + v4_fabric_init(&f, places, 4); + a = loaded(0); LIT(PB + 0); O(BANG_B); LIT(5); O(STORE_B); wait_for_ever(); /* writes to its port 0 */ + c = loaded(1); LIT(PB + 1); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever(); /* reads its port 1 */ + for (i = 0; i < 10; i++) (void)v4_fabric_step(&f); + CHECK(ND(a)->n.asking && ND(c)->n.reading, "one node is waiting to write and another to read"); + b = loaded(2); + LIT(PB + (v4_cell)V4_PORTS + 2); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); + LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); LIT(OUT + 1); O(BANG_A); O(STORE_A); + wait_for_ever(); + CHECK(v4_fabric_wire(&f, b, 2, a, 0) && v4_fabric_wire(&f, b, 4, c, 1), "a third is wired to both"); + { + static stream none; + static v4_device sink; + sink.take = stream_take; sink.give = 0; sink.self = &none; + CHECK(v4_fabric_wire_device(&f, b, 6, &sink), "and to a device that takes what is written to it"); + } + for (i = 0; i < 20; i++) (void)v4_fabric_step(&f); + CHECK(ND(b)->n.mem[OUT] == (1 << 2), "it sees which of its ports has a neighbour waiting to write to it: %ld", (long)ND(b)->n.mem[OUT]); + CHECK(ND(b)->n.mem[OUT + 1] == ((1 << 4) | (1 << 6)), "and which have one waiting to read from it, or a device: %ld", (long)ND(b)->n.mem[OUT + 1]); + CHECK(ND(a)->n.asking && ND(a)->n.request == 5 && ND(c)->n.reading && ND(c)->n.mem[OUT] == 0, "looking takes nothing and gives nothing: both wait as they did"); + CHECK(!ND(b)->n.stopped && ND(b)->n.reading, "and the one that looked did not wait to look"); + printf(" %d checks, %d failures\n", checks, failures); return failures != 0; } diff --git a/v4/tests/test_host_mesh.c b/v4/tests/test_host_mesh.c index e0e53a0b..5d608ef2 100644 --- a/v4/tests/test_host_mesh.c +++ b/v4/tests/test_host_mesh.c @@ -14,6 +14,7 @@ #include "v4/message.h" #include "v4/text.h" #include +#include #include #include "host_map.h" @@ -126,7 +127,7 @@ int main(void) nucleus.mem[CURRENT] = LATEST; nucleus.mem[SRC] = TIB; nucleus.mem[LINE_STATUS] = 1; - nucleus.mem[OUT_PTR] = OUT_W; + HOST_MESSAGES_EMPTY(&nucleus); v4_fabric_init(&f, places, 3); hera = starforth_node(0); @@ -196,22 +197,68 @@ int main(void) CHECK(strcmp(tell(12, "5 5 + ."), "10 ") == 0 && pool[1].es.anticlock == before, "text for it no longer passes through the middle node, which executes nothing"); } - /* ---- WHAT DOES NOT WORK: two neighbours that write to each other at once ---- + /* ---- two neighbours with something for each other at the same moment ---- * A write blocks until the neighbour reads, and a node that is writing - * is not reading. Here the far node sends text to the middle one and - * then goes to send word of how its own text ended, which goes by the - * middle one; the middle one does the text and goes to answer the far - * one. Each waits for the other to read, for ever. It is the same - * whenever messages go both ways on a wire at once, and it is because - * passing a message on is done here by the node's own programme - * (v4/capsule/quit.v4, (PASS-ON)), which cannot pass anything on while - * it waits to write. docs/v4.0.0/MESH.md, step 4. This check is here - * so that the fault is on record and is seen to go when it is put - * right. */ + * is not reading: so a node looks before it begins a message, and of + * two neighbours only the one with the lower number may wait to write + * to the other (v4/capsule/core.v4, (GATE); docs/v4.0.0/MESH.md 7a). + * Each node is told who is on each of its ports. */ CHECK(is_empty(tell(10, "NO-ROUTES 1 1 ROUTE 11 2 ROUTE 12 2 ROUTE")) && is_empty(tell(12, "NO-ROUTES 2 DEFAULT-ROUTE")), "the row of three again"); - (void)tell(12, ": HI S\" 72 EMIT\" 11 SEND ; HI"); - CHECK(pool[1].n.asking && pool[1].n.ask_port == 3 && pool[2].n.asking && pool[2].n.ask_port == 2, - "KNOWN FAULT: two neighbours each writing to the other wait for ever"); + CHECK(is_empty(tell(10, "11 2 NEIGHBOUR 0 3 NEIGHBOUR")) && is_empty(tell(11, "10 2 NEIGHBOUR 12 3 NEIGHBOUR")) && is_empty(tell(12, "11 2 NEIGHBOUR 0 3 NEIGHBOUR")), + "each is told who is on the other end of its ports"); + CHECK(strstr(tell(10, "11 8 NEIGHBOUR"), "Argument out of range") != NULL && strstr(tell(10, "11 -1 NEIGHBOUR"), "Argument out of range") != NULL, + "a port a node has not got is an error"); + /* the far node sends the middle one text, and then word of how its own + * text ended, which goes by the middle one; the middle one does the + * text and answers the far one: the two messages meet on one wire */ + CHECK(strcmp(tell(12, ": HI S\" 72 EMIT\" 11 SEND ; HI"), "H") == 0 && printed_from == 11 && ended == 1 && ended_from == 12, + "from the far end to the middle, with the answers crossing: \"%s\" from %ld", printed, (long)printed_from); + CHECK(waiting(hera) && waiting(mid) && waiting(far), "and all three are waiting again"); + + /* every node sends every other a number of messages, all at once: each + * counts what it is sent */ + CHECK(is_empty(tell(10, "VARIABLE GOT : HIT 1 GOT +! ; VARIABLE WHO VARIABLE SENT : BURST WHO ! 0 DO S\" HIT\" WHO @ SEND 1 SENT +! LOOP ;")) && + is_empty(tell(11, "VARIABLE GOT : HIT 1 GOT +! ; VARIABLE WHO VARIABLE SENT : BURST WHO ! 0 DO S\" HIT\" WHO @ SEND 1 SENT +! LOOP ;")) && + is_empty(tell(12, "VARIABLE GOT : HIT 1 GOT +! ; VARIABLE WHO VARIABLE SENT : BURST WHO ! 0 DO S\" HIT\" WHO @ SEND 1 SENT +! LOOP ;")), "each node is given a word to count with and one to send with"); + { + const char *r = tell(10, ": GO S\" 6 10 BURST 6 12 BURST\" 11 SEND S\" 6 11 BURST 6 10 BURST\" 12 SEND 6 11 BURST 6 12 BURST ; GO"); + CHECK(is_empty(r), "six messages from each node to each other, all at once: \"%s\"", r); + CHECK(waiting(hera) && waiting(mid) && waiting(far), "all three come to rest"); + CHECK(strcmp(tell(10, "GOT @ . (LOST) @ ."), "12 1 ") == 0 && strcmp(tell(11, "GOT @ . (LOST) @ ."), "12 0 ") == 0 && strcmp(tell(12, "GOT @ . (LOST) @ ."), "12 0 ") == 0, + "every one of them arrived, and none was let go: \"%s\"", printed); + } + + /* ---- more than a node has room to keep ---- + * A node that takes messages in while it waits to write keeps them, and + * when it has no room for one more it lets that one go and counts it. + * Far more are sent here than the nodes can keep. */ + { + long got = 0, lost = 0, sent = 0, v; + unsigned k; + static const v4_cell who[3] = { 10, 11, 12 }; + const char *r; + CHECK(is_empty(tell(10, "0 GOT ! 0 SENT !")) && is_empty(tell(11, "0 GOT ! 0 SENT !")) && is_empty(tell(12, "0 GOT ! 0 SENT !")), "the counts begin again"); + r = tell(10, ": GO2 S\" 200 10 BURST 200 12 BURST\" 11 SEND S\" 200 11 BURST 200 10 BURST\" 12 SEND 200 11 BURST 200 12 BURST ; GO2"); + CHECK(strcmp(r, "(still running)") != 0, "two hundred from each to each: it ends"); + CHECK(waiting(hera) && waiting(mid) && waiting(far), "and all three come to rest, none waiting to write"); + for (k = 0; k < 3; k++) { + r = tell(who[k], "GOT @ ."); + v = strtol(r, NULL, 10); got += v; + CHECK(v > 0 && v <= 400, "node %ld took %ld of the 400 for it", (long)who[k], v); + r = tell(who[k], "(LOST) @ ."); + lost += strtol(r, NULL, 10); + r = tell(who[k], "SENT @ ."); + sent += strtol(r, NULL, 10); + CHECK(pool[k].n.mem[MQ_COUNT] == 0, "node %ld has none left waiting", (long)who[k]); + } + lost -= 1; /* the one for node 99, earlier */ + /* what is let go is of every kind: the text that would have set a + * node sending, the answers to text, as well as what was sent */ + printf(" of %ld messages the nodes sent each other at once %ld arrived; %ld messages of all kinds were let go for want of room\n", sent, got, lost); + CHECK(lost > 0, "there was not room for all of them"); + CHECK(got <= sent && got + lost >= sent, "every message sent either arrived or was counted: %ld sent, %ld arrived, %ld let go", sent, got, lost); + CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && printed_from == 12, "and the nodes go on as before"); + } printf(" %d checks, %d failures\n", checks, failures); return failures != 0; diff --git a/v4/tests/test_host_quit.c b/v4/tests/test_host_quit.c index e06575bb..8f5351a8 100644 --- a/v4/tests/test_host_quit.c +++ b/v4/tests/test_host_quit.c @@ -160,7 +160,7 @@ static int text_ended; /* how the text ended, when it has */ /* 1 if the node is where it waits for a message, with nothing given it. */ static int node_idle(void) { - return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle + 3; + return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle + 8; /* in the first words of (IDLE), where it reads */ } /* Run until the text ends, or until it has been inside KEY with nothing to @@ -239,20 +239,21 @@ static void boot_with(unsigned depth) for (i = 0; i < depth; i++) v4_dstack_push(&n.ds, (v4_cell)(0x5A000000 + i)); v4_dstack_push(&n.ds, CANARY); v4_node_port_attach(&n, PORT); + v4_node_port_status(&n, 0, 1u | 1u << CONSOLE_PORT); /* the kernel and the console take what is written when it is written */ n.mem[WORD_DEFINED] = 0; /* no one is told of entries, until a test says so */ n.mem[WORD_FORGOTTEN] = 0; known_n = 0; last_request = 0; n.mem[LINE_STATUS] = 1; n.mem[ME] = 0; /* it has no number yet: a message to 0 is for it */ - n.mem[OUT_PTR] = OUT_W; + HOST_MESSAGES_EMPTY(&n); n.mem[REPLY] = PORT + CONSOLE_PORT; n.p = w_idle; /* waiting for a message */ typed_len = 0; line_open = 0; to_node.count = 0; to_sent = 0; from_node.count = 0; shown_len = 0; shown_dropped = 0; - (void)v4_exec_step_word(&n, &es, &h); /* it reads its ports, and is blocked */ + for (i = 0; i < 8 && !n.reading; i++) (void)v4_exec_step_word(&n, &es, &h); /* no message waits: it reads its ports, and is blocked */ } static void boot(void) { boot_with(0); } /* The same with nothing at all on the data stack. */ diff --git a/v4/tools/mkimage.c b/v4/tools/mkimage.c index 3e333119..ffbbf50b 100644 --- a/v4/tools/mkimage.c +++ b/v4/tools/mkimage.c @@ -99,7 +99,7 @@ int main(int argc, char **argv) if (!v4_node_guards_intact(&n)) die("the node's guards are damaged", NULL); n.mem[LINE_STATUS] = 1; /* nothing has ended badly */ - n.mem[OUT_PTR] = OUT_W; /* nothing has been printed */ + HOST_MESSAGES_EMPTY(&n); /* nothing has been printed, no message waits */ /* 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 */ diff --git a/v4/tools/post79_rules.py b/v4/tools/post79_rules.py index 300bc9c4..a0308877 100644 --- a/v4/tools/post79_rules.py +++ b/v4/tools/post79_rules.py @@ -67,6 +67,19 @@ STANDARD = { "MOVE.basic": dict(text="65 HERE ! 66 HERE 1+ ! HERE HERE 16 + 2 MOVE HERE 16 + @ . HERE 17 + @ . CR", out="65 66 \n", why="FORTH-79: MOVE moves cells; and " + D1), "PAD.usable": None, # see OPEN below + # see OPEN below: HERE handed to a word that takes a byte address + "C!.basic": None, + "C!.zero": None, + "C!.high_byte": None, + "C!.truncation": None, + "C@.after_cstore": None, + "C@.zero_byte": None, + "FILL.basic": None, + "FILL.zero_byte": None, + "CMOVE.overlap": None, + "TYPE.single_char": None, + "COUNT.string_bounds": None, + "COUNT.zero_addr_plus_one": None, "BASE.base_store": dict(out="255 \n", why="255 read in base 16 and printed in base 16 is 255; v3 printed 467"), @@ -123,9 +136,29 @@ STANDARD = { "LEAVE.qdloop": dict(out="3 \n", why=LEAVE), } +# Found 2026-10-06 (docs/v4.0.0/MESH.md section 10, step 4): on v4 HERE is a +# cell address and C! C@ FILL CMOVE COUNT TYPE take byte addresses, so these +# cases wrote into, or read from, the nucleus's own code at cell HERE/4 -- +# and passed, reading back what they had written there. Ruled: left out +# until how HERE and the byte words agree is settled, as its own step. +HERE_BYTE = ("OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): " + "the case wrote to or read from the nucleus's code at cell HERE/4") + # Dropped, and why. mkpost.py prints these with the cases it leaves out. DROPPED = { "PAD.usable": "OPEN: PAD 42 OVER ! faults on v4 -- PAD is a byte address and ! takes a cell address (D-1)", + "C!.basic": HERE_BYTE, + "C!.zero": HERE_BYTE, + "C!.high_byte": HERE_BYTE, + "C!.truncation": HERE_BYTE, + "C@.after_cstore": HERE_BYTE, + "C@.zero_byte": HERE_BYTE, + "FILL.basic": HERE_BYTE, + "FILL.zero_byte": HERE_BYTE, + "CMOVE.overlap": HERE_BYTE, + "TYPE.single_char": HERE_BYTE, + "COUNT.string_bounds": HERE_BYTE, + "COUNT.zero_addr_plus_one": HERE_BYTE, "CMOVE.bounds": "moves 1000 bytes over the system; v3 refuses by a bounds rule FORTH-79 has not got", } for _name in [k for k, v in STANDARD.items() if v is None and k.startswith("WORD.")]: