From 3d2dc041fbb2e214e37139560590d906b2998f5c Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Wed, 17 Jun 2020 21:41:01 -0400 Subject: [PATCH] Make word routines into word types Instead of having wordref point to core word routines, I made them into word 4 word types. It liberates space into the stable ABI and should make porting to other arches easier. I'm also thinking of combining word type with the namelen field for precious bytes saving, but not now... --- blk/070 | 2 +- blk/085 | 21 ++++++++++----------- blk/086 | 14 +++++++------- blk/243 | 4 +--- blk/263 | 2 +- blk/265 | 2 +- blk/283 | 10 ++++------ blk/284 | 2 +- blk/285 | 2 +- blk/301 | 14 ++++++-------- blk/302 | 4 +--- blk/303 | 3 +-- blk/371 | 2 +- blk/373 | 3 +-- blk/397 | 3 +-- emul/forth.bin | Bin 6180 -> 6188 bytes 16 files changed, 38 insertions(+), 50 deletions(-) diff --git a/blk/070 b/blk/070 index 3575ad3..6904e1b 100644 --- a/blk/070 +++ b/blk/070 @@ -2,7 +2,7 @@ Implementation notes 71 Execution model 73 Executing a word 75 Stack management 77 Dictionary -80 System variables 85 Word routines +80 System variables 85 Word types 89 Initialization sequence diff --git a/blk/085 b/blk/085 index 5395e9b..b41ca34 100644 --- a/blk/085 +++ b/blk/085 @@ -1,16 +1,15 @@ -Word routines +Word types -This is the description of all word routine you can encounter -in this Forth implementation. That is, a wordref will always -point to a memory offset containing one of these numbers. +There are 4 word types in Collapse OS. Whenever you have a +wordref, it's pointing to a byte with numbers 0 to 3. This +number is the word type and the word's behavior depends on it. -0x17: nativeWord. This words PFA contains native binary code -and is jumped to directly. +0: native. This words PFA contains native binary code and is +jumped to directly. -0x0e: compiledWord. This word's PFA contains an atom list and -its execution is described in "EXECUTION MODEL" above. +1: compiled. This word's PFA contains an atom list and its +execution is described in "EXECUTION MODEL" above. -0x0b: cellWord. This word is usually followed by a 2-byte value -in its PFA. Upon execution, the *address* of the PFA is pushed -to PS. +2: cell. This word is usually followed by a 2-byte value in its +PFA. Upon execution, the address of the PFA is pushed to PS. (cont.) diff --git a/blk/086 b/blk/086 index 745db83..d20b032 100644 --- a/blk/086 +++ b/blk/086 @@ -1,13 +1,13 @@ -0x2b: doesWord. This word is created by "DOES>" and is followed +3: DOES>. This word is created by "DOES>" and is followed by a 2-byte value as well as the address where "DOES>" was compiled. At that address is an atom list exactly like in a compiled word. Upon execution, after having pushed its cell -addr to PSP, it execute its reference exactly like a -compiledWord. - -Also note that word routines references in wordrefs are 1b. -This means that all word routine reference must live below -0x100 in boot binary. +addr to PSP, it executes its reference exactly like a +compiled word. + + + + diff --git a/blk/243 b/blk/243 index 24ddadd..6f1a1ce 100644 --- a/blk/243 +++ b/blk/243 @@ -8,9 +8,7 @@ : chkPS, L4 @ CALLnn, ; ( chkPS, B305 ) : CODE ( same as CREATE, but with native word ) - (entry) - 23 C, ( 23 == nativeWord ) -; + (entry) 0 C, ( 0 == native ) ; : ;CODE JPNEXT, ; diff --git a/blk/263 b/blk/263 index 9f2c2a9..b3a73be 100644 --- a/blk/263 +++ b/blk/263 @@ -2,7 +2,7 @@ VARIABLE XCURRENT : XCON XCURRENT CURRENT* ! ; : XCOFF 0x02 RAM+ CURRENT* ! ; : (xentry) XCON (entry) XCOFF ; -: XCREATE (xentry) 11 C, ; +: XCREATE (xentry) 2 C, ; : XCODE XCON CODE XCOFF ; : XIMM XCON IMMEDIATE XCOFF ; : _xapply ( a -- a-off ) diff --git a/blk/265 b/blk/265 index 0fc8380..cac8ccb 100644 --- a/blk/265 +++ b/blk/265 @@ -1,5 +1,5 @@ : X: - (xentry) [ 0x0e LITN ] C, + (xentry) 1 ( compiled ) C, BEGIN WORD XCURRENT @ SWAP ( xcur w ) _find ( a f ) IF ( a ) diff --git a/blk/283 b/blk/283 index 3b12696..9a18688 100644 --- a/blk/283 +++ b/blk/283 @@ -1,16 +1,14 @@ H@ ORG ! 0 JPnn, ( 00, main ) 0 JPnn, ( 03, find ) NOP, NOP, ( 06, unused ) NOP, NOP, ( 08, LATEST ) -NOP, ( 0a, unused ) -( 0b cellWord, push PFA ) DE PUSHqq, JR, 0x0c A, ( next ) -0 JPnn, ( 0e, compiledWord ) 0 JPnn, ( 11, pushRS ) -0 JPnn, ( 14, popRS ) -EXDEHL, JP(HL), NOP, ( 17, nativeWord ) +NOP, NOP, NOP, NOP, NOP, NOP, NOP, ( 0a, unused ) +0 JPnn, ( 11, pushRS ) 0 JPnn, ( 14, popRS ) +NOP, NOP, NOP, ( 17, unused ) 0 JPnn, ( 1a, next ) 0 JPnn, ( unused ) NOP, NOP, NOP, NOP, ( 20, unused ) NOP, NOP, NOP, NOP, ( 24, unused ) 0 JPnn, ( RST 28 ) -0 JPnn, ( 2b, doesWord ) NOP, NOP, ( 2e, unused ) +NOP, NOP, NOP, NOP, NOP, ( 2b, unused ) 0 JPnn, ( RST 30 ) 0 JPnn, ( 33, execute ) NOP, NOP, ( unused ) 0 JPnn, ( RST 38 ) diff --git a/blk/284 b/blk/284 index 122eb60..de85525 100644 --- a/blk/284 +++ b/blk/284 @@ -5,7 +5,7 @@ 0 A,, ( prev ) 4 A, H@ XCURRENT ! ( set current tip of dict, 0x42 ) - 0x17 A, ( nativeWord ) + 0 A, ( native ) 0x14 BCALL, ( popRS ) HL PUSHqq, IY POPqq, ( --> IP ) JPNEXT, diff --git a/blk/285 b/blk/285 index 94a215b..5ba13bb 100644 --- a/blk/285 +++ b/blk/285 @@ -6,7 +6,7 @@ CODE (?br) ( 0x67 ) ( True, skip next 2 bytes and don't branch ) IY INCss, IY INCss, JPNEXT, NOP, NOP, NOP, -CODE (loop) ( 0x77 ) +CODE (loop) ( 0x80 ) 0 IX+ INC(IXY+), IFZ, 1 IX+ INC(IXY+), THEN, ( I++ ) ( Jump if I <> I' ) A 0 IX+ LDrIXY, 2 IX- CP(IXY+), JRNZ, L2 BWR ( branch ) diff --git a/blk/301 b/blk/301 index 65f6bf2..7a6b45c 100644 --- a/blk/301 +++ b/blk/301 @@ -4,11 +4,9 @@ L3 BSET PC ORG @ 0x34 + ! ( execute. DE -> wordref ) BIN( @ [IF] A XORr, D ORr, IFZ, D BIN( @ 256 / LDrn, THEN, [THEN] - LDA(DE), - L A LDrr, - H BIN( @ 256 / LDrn, - DE INCss, - ( DE points to PFA ) - JP(HL), - - + LDA(DE), DE INCss, + A ORr, IFZ, EXDEHL, JP(HL), THEN, + A DECr, JRZ, L1 FWR ( compiled B303 ) + ( cell or does. push PFA ) DE PUSHqq, + A DECr, IFZ, JPNEXT, THEN, ( cell ) + ( continue to does, B302 ) diff --git a/blk/302 b/blk/302 index 8fa1aa4..cb37c5b 100644 --- a/blk/302 +++ b/blk/302 @@ -1,11 +1,9 @@ -PC ORG @ 0x2c + ! ( doesWord ) -( The word was spawned from a definition word that has a +( does. The word was spawned from a definition word that has a DOES>. PFA+2 (right after the actual cell) is a link to the slot right after that DOES>. Therefore, what we need to do push the cell addr like a regular cell, then follow the linkfrom the PFA, and then continue as a regular compiledWord. ) - DE PUSHqq, ( like a regular cell ) EXDEHL, HL INCss, HL INCss, diff --git a/blk/303 b/blk/303 index 6d8ca26..6aecef4 100644 --- a/blk/303 +++ b/blk/303 @@ -1,4 +1,4 @@ -PC ORG @ 0x0f + ! ( compiledWord ) +( compiled word ) L1 FSET ( execute B301 ) ( 1. Push current IP to RS 2. Set new IP to the second atom of the list 3. Execute the first atom of the list. ) @@ -13,4 +13,3 @@ PC ORG @ 0x0f + ! ( compiledWord ) - diff --git a/blk/371 b/blk/371 index cac2325..a9e00f2 100644 --- a/blk/371 +++ b/blk/371 @@ -6,7 +6,7 @@ H@ CURRENT ! ; : (entry) WORD [entry] ; -: CREATE (entry) 11 ( 11 == cellWord ) C, ; +: CREATE (entry) 2 ( cellWord ) C, ; : VARIABLE CREATE 2 ALLOT ; diff --git a/blk/373 b/blk/373 index 227c662..1eb9891 100644 --- a/blk/373 +++ b/blk/373 @@ -1,7 +1,6 @@ : DOES> ( Overwrite cellWord in CURRENT ) - ( 43 == doesWord ) - 43 CURRENT @ C! + 3 ( does ) CURRENT @ C! ( When we have a DOES>, we forcefully place HERE to 4 bytes after CURRENT. This allows a DOES word to use "," and "C," without messing everything up. ) diff --git a/blk/397 b/blk/397 index 7bbb860..9773573 100644 --- a/blk/397 +++ b/blk/397 @@ -7,8 +7,7 @@ ( gets its name at the very end. can't comment afterwards ) : _ BEGIN LIT< ) WORD S= UNTIL ; IMMEDIATE : _ ( : will get its name almost at the very end ) - (entry) - [ 14 ( == compiledWord ) LITN ] C, + (entry) 1 ( compiled ) C, BEGIN WORD FIND IF ( is word ) DUP IMMED? IF EXECUTE ELSE , THEN diff --git a/emul/forth.bin b/emul/forth.bin index 84b00f8c610125affc1e6cc00da4ab2ffc570d19..4f59a8afdf7907a58e6d957a4e83ad5805cb6eab 100644 GIT binary patch literal 6188 zcma)Ae{9s}d4In@?#@2j=kpH?h9unC#JSs%+7Y*7O)IrkS7ny?BTFTBidZ&Pnw_TAR3FF+Qy@eGnI%-aEjQsn@r(wzSp;>x z&*y#bfU43oTlRgQ=Y5{%eSSaB_hu`#*)5t7|Fc@CpV=eY?8`ui*}v9ib2vh|M|{ry zBaZPI-W5p-VTy~DVtzS0yH@!2kNRCeEI%)n`?ck2qB?*p6E>0k`g^|X%eBk3K;p+Y zoJt+_R|&hwY!x0OvsKHS5Sf|ZdS){xw9L$k=bthS>UYigmUgR77~)d)VwqULUrxe^ zzXI6YT@ZYQ*NyqhiJZ?qUnMG*tLK4hx7iWTqpjT-aD#sX2J-&pXLY8MSL`;?eJ@xE_M+%5`h=dY|=`}ylX_Y|#L*SW4? z?Tklve|ahEtD0Z8ZrzoI=WF}3uTA-kMQb7Jnfpm=_QOmqeqR!qW7>zA5s?|vXyAu- z-%CTo$Nk=KXp4LF8|n`4y_NSDN4G6x=Cfr2kJh%&9@@FK;(d?4@T)do$(1&b0i73W zYjGKDzIa6AwY7-GnyXj6UsA}`e_8Y6@UC#EFB$%oaEMD6uWD<4Va1b97@oa!7T}fn z4Em#^Ond1?kt{L#lf;;{!-&C>NR{m1)&j3=lbaRYQDu!$ubo^e8lGcl$3? zTqtig3!AlyHqRXrZ0?D7d0~g7dByGq*;g)H@z8;xo?cJh+wd6Y#`XukOyn! zaZ4i|Lh1&2ZY7?HbSKM@pJ$2eG;Kcnh1Ls?yYG=ZDo@w_?kVzIom!ne4b{Tb(n8#5 zGJ+x)i8YicSP*_DY=sqUim%#&BGhMD;aGBGnJ56yvybB3!lAusn$6wrTQ21O*+j(~x?S#n2WJ`@7L7J%fA#1Jqd0wQap4!*-9L&Ip#c5TMY+Fbf& z+k`Oz{Ih)#lz<6?XUw!H0Q-btx6cW?@5OzGNBn1PTlHI7P;>_4R`N+<)0)l4>_HK> zEOZrZPB?r=M-Pu21KlB{`z@^thiz*RpFcCJA-xrW!{aCBF!1Qd1kOvKxwQ!9rNzqY zCwbIH=(t_-w9)XB^0~Pksb*+Dlt6nc57G$RpnlSRU%L;YPj%ea-rBZWx|=6Ik|#fs zCq0o^71;j{pTQ8_-zL9PT;Ezb@B;L z0umQ9nv5AhzN7m_#|97jLrAxX!-DQJ&?_w(#jy0iHH4ntJS{9}(`1}6e&IMIigC+b zoSb$9#klx=uWk6p}|tq$s-WFPSFhHMu+LYjKWwIJU>^5@}Cb?QgHpwMk+4Dt@N zARnlHGfa6}{0EeA=8Oso;`ad>dVqfp#s;Aj4OG{@;P1#?JBe$XUm2E8P?s=7H1is= z+7J&-d$Yi$|4`oamadGWD@@UaDj65QT&a;}4fH>v+Li|@(BHqS!hV1KqbltG|ERFJ z@b^_XZz=`;EvJAo(ErD12LlDn-Jtl_+~@8>1sgedPS||MM}}kx7N7(Zv<5X5YVBx6 zCZ^$a)e}OkXnBBHm`={)R2N*w)+~>hzYl-Cxi=Etq8*){HY$$ry>2hL`SpKk(rIQ z5>IyI8znmi;7xgWyE7<~@#o@JSE+1bTXmb$rJr(EL*QxWqJBu+T0u@7-~X5t=z>uI z2UEwC0L{GVoG@-e2t|}Z&zsI`<^_m2?PTG+rQgGu1+A``HYd}!2@;sdr=)Nez)fcx ziuI;*+E6_Aob)wiMtXX}U6IaYxL*{?E`8Tz0;W+pi#byzNyp>R{jRK%ap|-MjBy>! zV-j^2(CP5a7~I;ZQG>F>62WdWYOrl}Mx*g$G1|ts?Nm`v)OWBexr)xh%6fL$%S9?J z9;Sop+*UjqeH`Yhcr%f%LAOC@#ZDSCzNPc37H)JKAf44Rh$H?7nyVkSGl^a>$3|b>TZO8KIGP=A7f;zZ65EVA=XNKb=|G<( z^4EaKRrC>Lg{&|lQv%Q5E~YZfr2bAT(ix1xiULh5A@&^XVrux@P{oDT$MC(5AW?-T zd3|@>+7V7Ngqb3jzOjVWFjcH>4fpgWcdr5KoG=H*ULE5=EJhb2jiTs^hZF7VK|z63 zdm0C9Tog~TB~b^>Lh)E4*%?c+uV^K8&NoUp&-X;S_hR8jK{0|D&wG;3gkyVMx~nK= z9ui-!Fhvw@1N1{RMt27AKEvC#%2EKri@lYIKgc86Rc&m`!(GYUEUvWJp2zqUMJr&c z0U>^>vCV~D$CSHHbmsdoupP#Hw6_f43ADcB>7`DF}ru1(oEWXg4FPyO(g5 z=nb*YP%)CmqIpI&Cmw20?uG{P9d+1-!k~nN%L>cc4qgOKH`hG9Vmzm|G2Zu)v;04ejFS#Qx z;i}?9S(xIl$|=(+Q-{lNT9m;4m&+NDcsZj>?@*x$ESi@wFG*kWAl2kj%q#o@NMpNw zx;%|nO|I5otSpsgZ8QSAKvy)@`UDc0g3e$Dv7sw*RHCXv;b+W~cC};WnKrb5iT#i- zV-v<5Th`WQoGY?}>?;*&-J)I(nVMQQBhbnw^-{9l%`A#W5=p+;OrhJlWejg)JNcUm zMm->I8!@!0Ciwyoka>F=IJO{d`%|f7t^P1VqgJFzD8NPVJjQ{-&=1jg zC%*uZ?fyCfr*=+0scn=`T(BmM>tf2qaYBg0BVMVfA9azVaw$j8DVcZNZBQ$@%t4oY zn_7aj1e`@G^)kzMETvDw_YoJ4lO|)VQlapDp-tm^99`$qpDJr~k5!I~B^XKztOgEy z#S)N{i0xtZD)gJcMwP(rP@5oIrnO29rB3fg^)?<*hh840@`qfIIEPtL>`WXqUtM({ z`x2l2d=;yfi+dH2)JSOn|qY6wtIVDax zm@7Y>ea5g>@pIZp;qzfmIx2@Na*NDI#dE?L!k2gZj*bn+6U>C4!2tvN54>>XyD&qQ zH)Fpddz~9y+p&MNG4=A$@W-Wm5$TFQiC&$?OBlO3Jsa;WmHmBOV%m(pD)wvi{n#qx zOd7GPa}t9uHNJr{qxsdA^991P-HjG_yZAh20+-)Iq6+2y26o^4l&r%Pv5TJ;zEhPD z|IXC!-YjoFB=5>^dfc8?K?gNe`l{6K9gZfj5pwb*K&@yiR!`Z=sdXgTTK!-uv4+l| z7~jTz84T};#MX$vM9m`;c63H!P5?^A6iQ!N*B47hqE+%hsggg~Qw}2?NXWf*n5NP- z6z}PcM3E+MVAXu5D({ayIXpJt@8Ezg`ojWnr!ms%oQic%{a&~D3(2PDQ106%Pr$?P bp*&s>cBT!UdJk72J_j}eCqa4SC20OHnI8qI literal 6180 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 o??^T^iPGOUxm(@*Cd%z`V~wkDSGl