PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/kernel.4

https://bitbucket.org/kt97679/relf
Forth | 1294 lines | 1038 code | 256 blank | 0 comment | 72 complexity | 155b29c34bc24080fdc73290d4ef705c MD5 | raw file
  1. \ This is the file kernel.4, included by the cross compiler to build
  2. \ machine-idependent binary image for RelF (Relative Forth).
  3. \ Based on SOD32 by L.C. Benschop
  4. \ Copyright 2001 - 2005, Kirill Timofeev, kt97679@gmail.com
  5. \ The program is released under the GNU General Public License version 2.
  6. \ There is NO WARRANTY.
  7. CROSS-COMPILE
  8. \ PART 0: BOOT VECTORS.
  9. COLD \ call to cold start location.
  10. WARM \ call to warm start location.
  11. DIV-EX \ address of division exception routine.
  12. BREAK-EX \ address of break handling routine.
  13. TIMER-EX \ address of timer interrupt routine.
  14. \ PART 1: LOW-LEVEL PRIMITIVES
  15. PRIMITIVE NOOP ( --- )
  16. \ Do nothing
  17. PRIMITIVE EXIT ( --- )
  18. \ Leave high-level definition
  19. PRIMITIVE LIT ( --- lit)
  20. \ Push literal on the stack (literal number is in-line).
  21. PRIMITIVE BRANCH ( --- )
  22. \ Unconditional jump (with inline argument)
  23. PRIMITIVE ?BRANCH ( --- )
  24. \ Conditional jump (with inline argument)
  25. PRIMITIVE DROP ( x ---)
  26. \ Discard the top item on the stack.
  27. PRIMITIVE DUP ( x --- x x )
  28. \ Duplicate the top cell on the stack.
  29. PRIMITIVE SWAP ( x1 x2 --- x2 x1 )
  30. \ Swap the two top items on the stack.
  31. PRIMITIVE ROT ( x1 x2 x3 --- x2 x3 x1 )
  32. \ Rotate the three top items on the stack.
  33. PRIMITIVE OVER ( x1 x2 --- x1 x2 x1)
  34. \ Copy the second cell of the stack.
  35. PRIMITIVE C@ ( c-addr --- c)
  36. \ Fetch character c at c-addr.
  37. PRIMITIVE @ ( a-addr --- x)
  38. \ Fetch cell x at a-addr.
  39. PRIMITIVE C! ( c c-addr --- )
  40. \ Store character c at c-addr
  41. PRIMITIVE ! ( x a-addr --- )
  42. \ Store cell x at a-addr
  43. PRIMITIVE AND ( x1 x2 --- x3)
  44. \ Bitwise and of the top two cells on the stack.
  45. PRIMITIVE OR ( x1 x2 --- x3)
  46. \ Bitwise or of the top two cells on the stack.
  47. PRIMITIVE XOR ( x1 x2 --- x3)
  48. \ Bitwise exclusive or of the top two cells on the stack.
  49. PRIMITIVE R> ( --- x)
  50. \ Pop the top of the return stack and place it on the stack.
  51. PRIMITIVE >R ( x ---)
  52. \ Push x on the return stack.
  53. PRIMITIVE R@ ( --- x)
  54. \ x is a copy of the top of the return stack.
  55. PRIMITIVE = ( x1 x2 --- f)
  56. \ f is true if and only if x1 equals x2.
  57. PRIMITIVE U< ( u1 u2 ---- f)
  58. \ f is true if and only if unsigned number u1 is less than u2.
  59. PRIMITIVE < ( n1 n2 --- f)
  60. \ f is true if and only if signed number n1 is less than n2.
  61. PRIMITIVE + ( w1 w2 --- w3)
  62. \ Add the top two numbers on the stack.
  63. PRIMITIVE NEGATE ( n1 --- -n1)
  64. \ Negate top number on the stack.
  65. PRIMITIVE LSHIFT ( x1 u --- x2)
  66. \ Shift x1 left by u bits, zeros are added to the right.
  67. PRIMITIVE RSHIFT ( x1 u --- x2)
  68. \ Shift x1 right by u bits, zeros are added to the left.
  69. PRIMITIVE UM* ( u1 u2 --- ud )
  70. \ Multiply two unsigned numbers, giving double result.
  71. PRIMITIVE UM/MOD ( ud u1 --- urem uquot)
  72. \ Divide the unsigned double number ud by u1, giving unsigned quotient
  73. \ and remainder.
  74. PRIMITIVE D+ ( d1 d2 --- d3)
  75. \ Add the double numbers d1 and d2.
  76. PRIMITIVE EMIT ( c ---)
  77. \ Output the character c to the terminal.
  78. PRIMITIVE KEY ( --- c)
  79. \ Input the character c from the terminal.
  80. PRIMITIVE BYE ( ---)
  81. \ Terminate the execution of Forth, return to host OS.
  82. PRIMITIVE SP@ ( --- a-addr)
  83. \ Return the address of the stack pointer (before SP@ was executed).
  84. PRIMITIVE SP! ( a-addr ---)
  85. \ Set the stack pointer to a-addr.
  86. PRIMITIVE RP@ ( --- a-addr)
  87. \ Return the address of the return stack pointer.
  88. PRIMITIVE RP! ( a-addr ---)
  89. \ Set the return stack pointer to a-addr.
  90. \ PART 2: RUNTIME PARTS THE VARIOUS DEFINITION CLASSES.
  91. \ Only VARIABLES (or CREATE) need a runtime part in this system.
  92. \ As this is a native code compiler, colon definitions have no runtime
  93. \ part and for CONSTANT it is compiled inline. For variables, a call
  94. \ to DOVAR is compiled. DOVAR pushes the return address (the address
  95. \ where the data of the variable is stored) on the stack.
  96. : DOVAR ( --- a-addr)
  97. \ Runtime part of variables.
  98. R> ;
  99. \ PART 3: SIMPLE DEFINITIONS
  100. \ This is a large class of words, which would be written in machine code
  101. \ on most non-native code systems. Many contain just a few words, so they
  102. \ are implemented as macros.
  103. \ This category contains simple arithmetic and compare words, the runtime
  104. \ parts of DO LOOP and string related words etc, many on which are
  105. \ dependent on each other, so they are in a less than logical order to
  106. \ avoid large numbers of forward references.
  107. : - ( w1 w2 --- w3 )
  108. \ Subtract the top two numbers on the stack (w2 from w1).
  109. NEGATE + ;
  110. : 0= ( x --- f)
  111. \ f is true if and only if x is 0.
  112. 0 = ;
  113. : <> ( x1 x2 --- f)
  114. \ f is true if and only if x1 is not equal to x2.
  115. = 0= ;
  116. : 0< ( n --- f)
  117. \ f is true if and only if n is less than 0.
  118. 0 < ;
  119. : > ( n1 n2 --- f)
  120. \ f is true if and only if the signed number n1 is less than n2.
  121. SWAP < ;
  122. : 0> ( n --- f)
  123. \ f is true if and only if n is greater than 0.
  124. 0 > ;
  125. : U> ( u1 u2 --- f)
  126. \ f is true if and only if the unsigned number u1 is greater than u2.
  127. SWAP U< ;
  128. : CR ( --- )
  129. \ Output a newline to the terminal.
  130. 13 EMIT 10 EMIT ;
  131. VARIABLE START \ holds absolute address of the system address space
  132. -1 CONSTANT TRUE
  133. VARIABLE S0 ( --- a-addr)
  134. \ Variable that holds the bottom address of the stack.
  135. VARIABLE R0 ( --- a-addr)
  136. \ Variable that holds the bottom address of the return stack.
  137. : DEPTH ( --- n )
  138. \ n is the number of cells on the stack (before DEPTH was executed).
  139. SP@ S0 @ SWAP - 2 RSHIFT ;
  140. : 2DUP ( d --- d d)
  141. \ Duplicate the top double number on the stack.
  142. OVER OVER ;
  143. : 2DROP ( d --- )
  144. \ Discard the top double number on the stack.
  145. DROP DROP ;
  146. \ The next few words manipulate addresses in a system-independent way.
  147. \ Use CHAR+ instead of 1+ and it will be portable to systems where you
  148. \ have to add something different from 1.
  149. : CHAR+ ( c-addr1 --- c-addr2)
  150. \ c-addr2 is the next character address after c-addr1.
  151. 1 + ;
  152. : CHARS ( n1 --- n2)
  153. \ n2 is the number of address units occupied by n1 characters.
  154. ; \ A no-op.
  155. : CHAR- ( c-addr1 --- c-addr2)
  156. \ c-addr2 is the previous character address before c-addr1.
  157. -1 + ;
  158. : CELL+ ( a-addr1 --- a-addr2)
  159. \ a-addr2 is the address of the next cell after a-addr2.
  160. 4 + ;
  161. : CELLS ( n2 --- n1)
  162. \ n2 is the number of address units occupied by n1 cells.
  163. 2 LSHIFT ;
  164. : CELL- ( a-addr1 --- a-addr2)
  165. \ a-addr2 is the address of the previous cell before a-addr1.
  166. -4 + ;
  167. \ The DO LOOP related words use the return stack. The top of the
  168. \ return stack is the loop counter (I) and the next cell is the limit.
  169. \ (LOOP) and (+LOOP) are followed by an inline loop start address.
  170. \ (?DO) and (LEAVE) are followed by an inline leave address.
  171. \ The inline parameters are accessed through the return stack.
  172. \ They can 'jump' by returning to a different address.
  173. \ These words are called 'subroutines', not macros.
  174. \ To access the loop parameters on the return stack, the DO LOOP words must
  175. \ first pop their OWN return address!
  176. : (DO) ( n1 n2 ---)
  177. \ Runtime part of DO.
  178. R> ROT ROT SWAP >R >R >R ;
  179. : (?DO) ( n1 n2 ---)
  180. \ Runtime part of ?DO
  181. 2DUP - IF R> ROT ROT SWAP >R >R CELL+ >R
  182. ELSE 2DROP R> @ >R \ Jump to leave address if equal
  183. THEN ;
  184. : I ( --- n )
  185. \ Return the counter (index) of the innermost DO LOOP
  186. R> R@ SWAP >R ;
  187. : J ( --- n)
  188. \ Return the counter (index) of the next loop outer to the innermost DO LOOP
  189. RP@ 12 + @ ;
  190. : (LEAVE) ( --- )
  191. \ Runtime part of LEAVE
  192. R> @ R> DROP R> DROP >R ; \ Remove loop parameters and replace top of ret
  193. \ stack by leave address.
  194. : UNLOOP ( --- )
  195. \ Remove one set of loop parameters from the return stack.
  196. R> R> R> 2DROP >R ;
  197. : (LOOP) ( ---)
  198. \ Runtime part of LOOP
  199. R> R> 1 + DUP R@ = \ Add 1 to count and compare to limit.
  200. IF
  201. R> 2DROP CELL+ >R \ Discard parameters and skip leave address.
  202. ELSE
  203. >R DUP @ + >R \ Repush counter and jump to loop start address.
  204. THEN ;
  205. : (+LOOP) ( n ---)
  206. \ Runtime part of +LOOP
  207. \ Very similar to (LOOP), but the compare condition is different.
  208. \ exit if ( oldcount - lim < 0) xor ( newcount - lim < 0).
  209. R> SWAP R> DUP R@ - ROT ROT + DUP R@ - ROT XOR 0 <
  210. IF R> 2DROP CELL+ >R
  211. ELSE >R DUP @ + >R THEN ;
  212. : COUNT ( c-addr1 --- c-addr2 c)
  213. \ c-addr2 is the next address after c-addr1 and c is the character
  214. \ stored at c-addr1.
  215. \ This word is intended to be used with 'counted strings' where the
  216. \ first character indicates the length of the string.
  217. DUP 1 + SWAP C@ ;
  218. : TYPE ( c-addr1 u --- )
  219. \ Output the string starting at c-addr and length u to the terminal.
  220. OVER + SWAP BEGIN 2DUP - WHILE DUP C@ EMIT CHAR+ REPEAT 2DROP ;
  221. : ALIGNED ( c-addr --- a-addr )
  222. \ a-addr is the first aligned address after c-addr.
  223. 3 + -4 AND ;
  224. : (.") ( --- )
  225. \ Runtime part of ."
  226. \ This expects an in-line counted string.
  227. R> COUNT 2DUP TYPE + ALIGNED >R ;
  228. : (S") ( --- c-addr u )
  229. \ Runtime part of S"
  230. \ It returns address and length of an in-line counted string.
  231. R> COUNT 2DUP + ALIGNED >R ;
  232. 32 CONSTANT BL ( --- 32 )
  233. \ Constant 32, the blank character
  234. : PICK ( u --- x)
  235. \ place a copy of stack cell number u on the stack. 0 PICK is DUP, 1 PICK
  236. \ is OVER etc.
  237. 1 + CELLS SP@ + @ ;
  238. : 1+ ( w1 --- w2 )
  239. \ Add 1 to the top of the stack.
  240. 1 + ;
  241. : 1- ( w1 --- w2)
  242. \ Subtract 1 from the top of the stack.
  243. -1 + ;
  244. : INVERT ( x1 --- x2)
  245. \ Invert all the bits of x1 (one's complement)
  246. -1 XOR ;
  247. : 2* ( w1 --- w2)
  248. \ Multiply w1 by 2.
  249. 1 LSHIFT ;
  250. : 2/ ( n1 --- n2)
  251. \ Divide signed number n1 by 2.
  252. DUP $80000000 AND SWAP 1 RSHIFT OR ;
  253. : +! ( w a-addr ---)
  254. \ Add w to the contents of the cell at a-addr.
  255. DUP @ ROT + SWAP ! ;
  256. \ Double numbers occupy two cells in memory and on the stack.
  257. \ The most significant half on the number is in the first memory
  258. \ cell or in the top cell on the stack (which is also the first address).
  259. : 2@ ( a-addr --- d )
  260. \ Fetch double number d at a-addr.
  261. DUP CELL+ @ SWAP @ ;
  262. : 2! ( d a-addr --- )
  263. \ Store the double number d at a-addr.
  264. DUP >R ! R> CELL+ ! ;
  265. : ?DUP ( n --- 0 | n n)
  266. \ Duplicate the top cell on the stack, but only if it is nonzero.
  267. DUP IF DUP THEN ;
  268. : MIN ( n1 n2 --- n3)
  269. \ n3 is the minimum of n1 and n2.
  270. 2DUP > IF SWAP THEN DROP ;
  271. : MAX ( n1 n2 --- n3)
  272. \ n3 is the maximum of n1 and n2.
  273. 2DUP < IF SWAP THEN DROP ;
  274. : DNEGATE ( d1 --- d2)
  275. \ Negate the top double number on the stack.
  276. >R NEGATE R> NEGATE OVER 0= 0= + ;
  277. : ABS ( n --- u)
  278. \ u is the absolute value of n.
  279. DUP 0< IF NEGATE THEN ;
  280. : DABS ( d --- ud)
  281. \ ud is the absolute value of d.
  282. DUP 0< IF DNEGATE THEN ;
  283. : SM/REM ( d n1 --- nrem nquot )
  284. \ Divide signed double number d by single number n1, giving quotient and
  285. \ remainder. Round towards zero, remainder has same sign as dividend.
  286. 2DUP XOR >R OVER >R \ Push signs of quot and rem.
  287. ABS >R DABS R>
  288. UM/MOD
  289. SWAP R> 0< IF NEGATE THEN SWAP
  290. R> 0< IF NEGATE THEN ;
  291. : FM/MOD ( d n1 --- nrem nquot )
  292. \ Divide signed double number d by single number n1, giving quotient and
  293. \ remainder. Round always down (floored division),
  294. \ remainder has same sign as divisor.
  295. DUP >R 2DUP XOR >R
  296. SM/REM
  297. OVER R> 0< AND IF SWAP R@ + SWAP 1 - THEN R> DROP ;
  298. : M* ( n1 n2 --- d )
  299. \ Multiply the signed numbers n1 and n2, giving the signed double number d.
  300. 2DUP XOR >R ABS SWAP ABS UM* R> 0< IF DNEGATE THEN ;
  301. : * ( w1 w2 --- w3)
  302. \ Multiply single numbers, signed or unsigned give the same result.
  303. UM* DROP ;
  304. : */MOD ( n1 n2 n3 --- nrem nquot)
  305. \ Multiply signed numbers n1 by n2 and divide by n3, giving quotient and
  306. \ remainder. Intermediate result is double.
  307. >R M* R> FM/MOD ;
  308. : */ ( n1 n2 n3 --- n4 )
  309. \ Multiply signed numbers n1 by n2 and divide by n3, giving quotient n4.
  310. \ Intermediate result is double.
  311. */MOD SWAP DROP ;
  312. : S>D ( n --- d)
  313. \ Convert single number to double number.
  314. DUP 0< ;
  315. : /MOD ( n1 n2 --- nrem nquot)
  316. \ Divide signed number n1 by n2, giving quotient and remainder.
  317. SWAP S>D ROT FM/MOD ;
  318. : / ( n1 n2 --- n3)
  319. \ n3 is n1 divided by n2.
  320. /MOD SWAP DROP ;
  321. : MOD ( n1 n2 --- n3)
  322. \ n3 is the remainder of n1 and n2.
  323. /MOD DROP ;
  324. : EXECUTE ( xt ---)
  325. \ Execute the word with execution token xt.
  326. \ Return from EXECUTE goes to xt pushed on the ret stack by >R, return from
  327. \ the word x1 returns to definition that calls EXECUTE
  328. >R ;
  329. \ PART 4: NUMERIC OUTPUT WORDS.
  330. VARIABLE BASE ( --- a-addr)
  331. \ Variable that contains the numerical conversion base.
  332. VARIABLE DP ( --- a-addr)
  333. \ Variable that contains the dictionary pointer. New space is allocated
  334. \ from the address in DP
  335. VARIABLE HLD ( --- a-addr)
  336. \ Variable that holds the address of the numerical output conversion
  337. \ character.
  338. VARIABLE DPL ( --- a-addr)
  339. \ Variable that holds the decimal point location for numerical conversion.
  340. : DECIMAL ( --- )
  341. \ Set numerical conversion to decimal.
  342. 10 BASE ! ;
  343. : HEX ( --- )
  344. \ Set numerical conversion to hexadecimal.
  345. 16 BASE ! ;
  346. : SPACE ( ---)
  347. \ Output a space to the terminal.
  348. BL EMIT ;
  349. : SPACES ( u --- )
  350. \ Output u spaces to the terminal.
  351. BEGIN DUP WHILE SPACE 1- REPEAT DROP ;
  352. : HERE ( --- c-addr )
  353. \ The address of the dictionary pointer. New space is allocated here.
  354. DP @ ;
  355. : PAD ( --- c-addr )
  356. \ The address of a scratch pad area. Right below this address there is
  357. \ the numerical conversion buffer.
  358. DP @ 84 + ;
  359. : MU/MOD ( ud u --- urem udquot )
  360. \ Divide unsigned double number ud by u and return a double quotient and
  361. \ a single remainder.
  362. >R 0 R@ UM/MOD R> SWAP >R UM/MOD R>
  363. ;
  364. \ The numerical conversion buffer starts right below PAD and grows down.
  365. \ Characters are added to it from right to left, as as the div/mod algorithm
  366. \ to convert numbers to an arbitrary base produces the digits from right to
  367. \ left.
  368. : HOLD ( c ---)
  369. \ Insert character c into the numerical conversion buffer.
  370. -1 HLD +! HLD @ C! ;
  371. : # ( ud1 --- ud2)
  372. \ Extract the rightmost digit of ud1 and put it into the numerical
  373. \ conversion buffer.
  374. BASE @ MU/MOD ROT DUP 9 > IF 7 + THEN 48 + HOLD ;
  375. : #S ( ud --- 0 0 )
  376. \ Convert ud by repeated use of # until ud is zero.
  377. BEGIN # 2DUP OR 0= UNTIL ;
  378. : SIGN ( n ---)
  379. \ Insert a - sign in the numerical conversion buffer if n is negative.
  380. 0< IF 45 HOLD THEN ;
  381. : <# ( --- )
  382. \ Reset the numerical conversion buffer.
  383. PAD HLD ! ;
  384. : #> ( ud --- addr u )
  385. \ Discard ud and give the address and length of the numerical conversion
  386. \ buffer.
  387. 2DROP HLD @ PAD OVER - ;
  388. : D. ( d --- )
  389. \ Type the double number d to the terminal.
  390. SWAP OVER DABS <# #S ROT SIGN #> TYPE SPACE ;
  391. : U. ( u ---)
  392. \ Type the unsigned number u to the terminal.
  393. 0 D. ;
  394. : . ( n ---)
  395. \ Type the signed number n to the terminal.
  396. S>D D. ;
  397. \ PART 5: MEMORY BLOCK MOVE AND RELATED WORDS.
  398. : CMOVE ( c-addr1 c-addr2 u --- )
  399. \ Copy u bytes starting at c-addr1 to c-addr2, proceeding in ascending
  400. \ order.
  401. DUP IF >R
  402. BEGIN
  403. OVER C@ SWAP DUP >R C! R> 1 + SWAP 1 + SWAP
  404. R> -1 + DUP >R 0=
  405. UNTIL
  406. R>
  407. THEN
  408. 2DROP DROP
  409. ;
  410. : CMOVE> ( c-addr1 c-addr2 u --- )
  411. \ Copy a block of u bytes starting at c-addr1 to c-addr2, proceeding in
  412. \ descending order.
  413. DUP IF >R R@ + -1 + SWAP R@ + -1 + SWAP
  414. BEGIN
  415. OVER C@ SWAP DUP >R C! R> -1 + SWAP -1 + SWAP
  416. R> -1 + DUP >R 0=
  417. UNTIL
  418. R>
  419. THEN
  420. 2DROP DROP
  421. ;
  422. \ It's here because it needs CMOVE>
  423. : ROLL ( u ---)
  424. \ Move stack cell number u to the top. 1 ROLL is SWAP, 2 ROLL is ROT etc.
  425. 1 + CELLS DUP SP@ + CELL+ @ SWAP
  426. SP@ CELL+ DUP CELL+ ROT CMOVE> DROP ;
  427. : MOVE ( c-addr1 c-addr2 u --- )
  428. \ Copy a block of u bytes starting at c-addr1 to c-addr2. Order is such
  429. \ that partially overlapping blocks are copied intact.
  430. >R 2DUP U< IF R> CMOVE> ELSE R> CMOVE THEN ;
  431. : FILL ( c-addr u c ---)
  432. \ Fill a block of u bytes starting at c-addr with character c.
  433. OVER IF >R
  434. BEGIN
  435. R@ ROT DUP >R C! R> 1 + SWAP
  436. -1 + DUP 0=
  437. UNTIL
  438. R>
  439. THEN
  440. 2DROP DROP
  441. ;
  442. : 2OVER ( d1 d2 --- d1 d2 d1)
  443. \ Take a copy of the second double number of the stack and push it on the
  444. \ stack.
  445. 03 PICK 03 PICK ;
  446. : 2SWAP ( d1 d2 --- d2 d1)
  447. \ Swap the top two double numbers on the stack.
  448. 03 ROLL 03 ROLL ;
  449. \ PART 6: FILE ACCESS WORDS.
  450. 00 CONSTANT W/O ( --- mode)
  451. \ Read only file access mode.
  452. 02 CONSTANT R/O ( --- mode)
  453. \ Write only file access mode.
  454. 04 CONSTANT R/W ( --- mode)
  455. \ Read write file access mode.
  456. : BIN ( mode1 --- mode2)
  457. \ Modify the R/O W/O or R/W mode so that it applies to binary files.
  458. 1 + ;
  459. PRIMITIVE OPEN-FILE
  460. PRIMITIVE CLOSE-FILE
  461. PRIMITIVE READ-LINE
  462. PRIMITIVE WRITE-LINE
  463. PRIMITIVE READ-FILE
  464. PRIMITIVE WRITE-FILE
  465. PRIMITIVE SYSTEM
  466. PRIMITIVE REPOSITION-FILE
  467. PRIMITIVE FILE-POSITION
  468. PRIMITIVE DELETE-FILE
  469. PRIMITIVE FILE-SIZE
  470. : CREATE-FILE ( c-addr u mode --- fid ior)
  471. \ Create a new file with the name starting at c-addr with length u.
  472. \ Return the file-ID and the IO result. (ior=0 if success)
  473. 1 AND OPEN-FILE ;
  474. \ : READ-LINE >R OVER R> READ-FILE >R OVER + SWAP
  475. \ BEGIN WHILE REPEAT
  476. \ R> ;
  477. \ PART 7: SOURCE INPUT WORDS.
  478. : ACCEPT ( c-addr n1 --- n2 )
  479. \ Read a line from the terminal to a buffer starting at c-addr with
  480. \ length n1. n2 is the number of characters read,
  481. >R 0
  482. BEGIN
  483. KEY DUP 8 = OVER 127 = OR
  484. IF \ Backspace/del
  485. DROP DUP IF 1- THEN
  486. ELSE
  487. DUP 10 = OVER 13 = OR
  488. IF \ CR/LF
  489. DROP SWAP DROP R> DROP EXIT
  490. ELSE
  491. OVER R@ - IF
  492. >R OVER OVER + R> SWAP C! 1+
  493. ELSE
  494. DROP
  495. THEN
  496. THEN
  497. THEN
  498. 0 UNTIL
  499. ;
  500. VARIABLE TIB ( --- addr)
  501. \ is the standard terminal input buffer.
  502. 80 CHARS-T ALLOT-T
  503. VARIABLE SPAN ( --- addr)
  504. \ This variable holds the number of characters read by EXPECT.
  505. VARIABLE #TIB ( --- addr)
  506. \ This variable holds the number of characters in the terminal input buffer.
  507. VARIABLE >IN ( --- addr)
  508. \ This variable holds an index in the current input source where the next word
  509. \ will be parsed.
  510. VARIABLE SID ( --- addr)
  511. \ This variable holds the source i.d. returned by SOURCE-ID.
  512. VARIABLE SRC ( --- addr)
  513. \ This variable holds the address of the current input source.
  514. VARIABLE #SRC ( --- addr)
  515. \ This variable holds the length of the current input source.
  516. : EXPECT ( c-addr u --- )
  517. \ Read a line from the terminal to a buffer at c-addr with length u.
  518. \ Store the length of the line in SPAN.
  519. ACCEPT SPAN ! ;
  520. : QUERY ( --- )
  521. \ Read a line from the terminal into the terminal input buffer.
  522. TIB 80 ACCEPT #TIB ! ;
  523. : SOURCE ( --- addr len)
  524. \ Return the address and length of the current input source.
  525. SRC @ #SRC @ ;
  526. : SOURCE-ID ( --- sid)
  527. \ Return the i.d. of the current source i.d., 0 for terminal, -1
  528. \ for EVALUATE and positive number for INCLUDE file.
  529. SID @ ;
  530. : REFILL ( --- f)
  531. \ Refill the current input source when it is exhausted. f is
  532. \ true if it was successfully refilled.
  533. SOURCE-ID -1 = IF
  534. 0 \ Not refillable for EVALUATE
  535. ELSE
  536. SOURCE-ID IF
  537. SRC @ 256 SOURCE-ID READ-LINE ABORT" Error reading file"
  538. SWAP #SRC ! 0 >IN !
  539. #SRC @ IF SOURCE OVER + SWAP
  540. BEGIN 2DUP - WHILE DUP C@ 9 = IF BL OVER C! THEN 1+ REPEAT 2DROP THEN
  541. \ Change tabs to space.
  542. \ flag from READ-LINE is returned (no success at EOF)
  543. ELSE
  544. QUERY #TIB @ #SRC ! 0 >IN ! -1 \ Always successful from terminal.
  545. THEN
  546. THEN
  547. ;
  548. : SCAN ( c-addr1 u1 c --- c-addr2 u2 )
  549. \ Find the first occurrence of character c in the string c-addr1 u1
  550. \ c-addr2 u2 is the remaining part of the string starting with that char.
  551. \ It is a zero-length string if c was not found.
  552. BEGIN
  553. OVER
  554. WHILE
  555. ROT DUP C@ >R OVER R> =
  556. IF
  557. ROT ROT DROP EXIT
  558. THEN
  559. 1+ ROT ROT SWAP 1- SWAP
  560. REPEAT DROP
  561. ;
  562. : SKIP ( c-addr1 u1 c --- c-addr2 u2 )
  563. \ Find the first character not equal to c in the string c-addr1 u1
  564. \ c-addr2 u2 is the remaining part of the string starting with the
  565. \ nonmatching char. It is a zero-length string if no other chars found.
  566. BEGIN
  567. OVER
  568. WHILE
  569. ROT DUP C@ >R OVER R> -
  570. IF
  571. ROT ROT DROP EXIT
  572. THEN
  573. 1+ ROT ROT SWAP 1- SWAP
  574. REPEAT DROP
  575. ;
  576. : PARSE ( c --- addr len )
  577. \ Find a character sequence in the current source that is delimited by
  578. \ character c. Adjust >IN to 1 past the end delimiter character.
  579. >R SOURCE >IN @ - SWAP >IN @ + R> OVER >R >R SWAP
  580. R@ SKIP OVER R> SWAP >R SCAN IF 1 >IN +! THEN
  581. DUP R@ - R> SWAP
  582. ROT R> - >IN +! ;
  583. : PLACE ( addr len c-addr --- )
  584. \ Place the string starting at addr with length len at c-addr as
  585. \ a counted string.
  586. 2DUP C! CHAR+ SWAP CMOVE ;
  587. : WORD ( c --- addr )
  588. \ Parse a character sequence delimited by character c and return the
  589. \ address of a counted string that is a copy of it. The counted
  590. \ string is actually placed at HERE. The character after the counted
  591. \ string is set to a space.
  592. PARSE HERE PLACE HERE BL HERE COUNT + C! ;
  593. \ PART 8: INTERPRETER HELPER WORDS
  594. \ First we need FIND and related words.
  595. \ Each word list consists of one linked list of definitions. No hashing
  596. \ is used to speed up dictionary search. All names in the dictionary
  597. \ are at aligned addresses and FIND is optimized to compare one 4-byte
  598. \ cell at a time.
  599. \ Dictionary definitions are built as follows:
  600. \
  601. \ LINK field: 1 cell, aligned, contains name field of previous word in list.
  602. \ NAME field: counted string of at most 31 characters.
  603. \ bits 5-7 of length byte have special meaning.
  604. \ 7 is always set to mark start of name ( for >NAME)
  605. \ 6 is set if the word is immediate.
  606. \ 5 is set if the word is a macro.
  607. \ CODE field: first aligned address after name, is execution token for word.
  608. \ here the executable code for the word starts. (is 1 cell for
  609. \ variables etc.)
  610. \ PARAMETER field: (body) Contains the data of constants and variables etc.
  611. VARIABLE NAMEBUF ( --- a-addr)
  612. \ An aligned buffer that holds a copy of the name that is searched.
  613. 28 ALLOT-T
  614. VARIABLE FORTH-WORDLIST ( --- addr)
  615. \ This variable holds a pointer to the last definition in the Forth
  616. \ word list.
  617. VARIABLE LAST ( --- addr)
  618. \ This variable holds a pointer to the last definition created.
  619. VARIABLE CONTEXT 28 ALLOT-T ( --- a-addr)
  620. \ This variable holds the addresses of up to 8 word lists that are
  621. \ in the search order.
  622. VARIABLE #ORDER ( --- addr)
  623. \ This variable holds the number of word list that are in the search order.
  624. VARIABLE CURRENT ( --- addr)
  625. \ This variable holds the address of the word list to which new definitions
  626. \ are added.
  627. : NAME>BUF ( c-addr u ---)
  628. \ Move the name c-addr u to the aligned buffer NAMEBUF.
  629. NAMEBUF 32 0 FILL 32 MIN NAMEBUF PLACE ;
  630. : SEARCH-WORDLIST ( c-addr u wid --- 0 | xt 1 xt -1)
  631. \ Search the wordlist with address wid for the name c-addr u.
  632. \ Return 0 if not found, the execution token xt and -1 for non-immediate
  633. \ words and xt and 1 for immediate words.
  634. ROT ROT
  635. NAME>BUF
  636. @ DUP IF
  637. BEGIN
  638. NAMEBUF @ OVER @ $1FFFFFFF AND = \ Compare first cells of names.
  639. \ Mask off bits 5-7 of name length, these have special purposes.
  640. IF
  641. NAMEBUF CELL+
  642. OVER CELL+
  643. 4 >R
  644. BEGIN
  645. OVER @ OVER @ - >R
  646. CELL+ SWAP CELL+ SWAP R> R> CELL+ >R \ Compare all of name until different
  647. UNTIL
  648. R> NAMEBUF C@ 1 + ALIGNED CELL+ = IF \ match to the right length then found
  649. SWAP DROP SWAP C@
  650. 64 AND IF \ determine immediateness of a word from bit 6 of name length.
  651. 1
  652. ELSE
  653. -1
  654. THEN
  655. SWAP -4 + SWAP EXIT \ Exit with execution token and flag.
  656. THEN
  657. 2DROP
  658. THEN
  659. -4 + DUP @ DUP ROT ROT + SWAP 0 = \ Take address of next word from link field until 0
  660. UNTIL
  661. THEN
  662. DROP 0 \ Not found.
  663. ;
  664. : FIND ( c-addr --- c-addr 0| xt 1|xt -1 )
  665. \ Search all word lists in the search order for the name in the
  666. \ counted string at c-addr. If not found return the name address and 0.
  667. \ If found return the execution token xt and -1 if the word is non-immediate
  668. \ and 1 if the word is immediate.
  669. #ORDER @ DUP 1 > IF
  670. CONTEXT #ORDER @ 1- CELLS + DUP @ SWAP 4 - @ =
  671. ELSE 0 THEN
  672. IF 1- THEN \ If last wordlist is double, don't search it twice.
  673. BEGIN
  674. DUP
  675. WHILE
  676. 1- >R
  677. DUP COUNT
  678. R@ CELLS CONTEXT + @ SEARCH-WORDLIST
  679. DUP
  680. IF
  681. R> DROP ROT DROP EXIT \ Exit if found.
  682. THEN
  683. DROP R>
  684. REPEAT
  685. ;
  686. \ The following words are related to numeric input.
  687. : DIGIT? ( c -- 0| c--- n -1)
  688. \ Convert character c to its digit value n and return true if c is a
  689. \ digit in the current base. Otherwise return false.
  690. 48 - DUP 0< IF DROP 0 EXIT THEN
  691. DUP 9 > OVER 17 < AND IF DROP 0 EXIT THEN
  692. DUP 9 > IF 7 - THEN
  693. DUP BASE @ < 0= IF DROP 0 EXIT THEN
  694. -1
  695. ;
  696. : >NUMBER ( ud1 c-addr1 u1 --- ud2 c-addr2 u2 )
  697. \ Convert the string at c-addr with length u1 to binary, multiplying ud1
  698. \ by the number in BASE and adding the digit value to it for each digit.
  699. \ c-addr2 u2 is the remainder of the string starting at the first character
  700. \ that is no digit.
  701. BEGIN
  702. DUP
  703. WHILE
  704. -1 + >R
  705. COUNT DIGIT? 0=
  706. IF
  707. R> 1+ SWAP -1 + SWAP EXIT
  708. THEN
  709. SWAP >R
  710. >R
  711. SWAP BASE @ UM* ROT BASE @ * 0 SWAP D+ \ Multiply ud by base.
  712. R> 0 D+ \ Add new digit.
  713. R> R>
  714. REPEAT
  715. ;
  716. : CONVERT ( ud1 c-addr1 --- ud2 c-addr2)
  717. \ Convert the string starting at c-addr1 + 1 to binary. c-addr2 is the
  718. \ address of the first non-digit. Digits are added into ud1 as in >NUMBER
  719. -1 + -1 >NUMBER DROP ;
  720. : NUMBER? ( c-addr ---- d f)
  721. \ Convert the counted string at c-addr to a double binary number.
  722. \ f is true if and only if the conversion was successful. DPL contains
  723. \ -1 if there was no point in the number, else the position of the point
  724. \ from the right. Special prefixes: # means decimal, $ means hex.
  725. -1 DPL !
  726. BASE @ >R
  727. COUNT
  728. OVER C@ 45 = DUP >R IF -1 + SWAP 1 + SWAP THEN \ Get any - sign
  729. OVER C@ 36 = IF 16 BASE ! -1 + SWAP 1 + SWAP THEN \ $ sign for hex.
  730. OVER C@ 35 = IF 10 BASE ! -1 + SWAP 1 + SWAP THEN \ # sign for decimal
  731. DUP 0 > 0= IF R> DROP R> BASE ! 0 EXIT THEN \ Length 0 or less?
  732. >R >R 0 0 R> R>
  733. BEGIN
  734. >NUMBER
  735. DUP IF OVER C@ 46 = IF -1 + DUP DPL ! SWAP 1 + SWAP ELSE \ handle point.
  736. R> DROP R> BASE ! 0 EXIT THEN \ Error if anything but point
  737. THEN
  738. DUP 0= UNTIL 2DROP R> IF DNEGATE THEN
  739. R> BASE ! -1
  740. ;
  741. \ PART 9: THE COMPILER
  742. : (ABORT") ( f --- )
  743. \ Runtime part of ABORT"
  744. IF R> COUNT TYPE SPACE HERE COUNT TYPE CR WARM
  745. ELSE R> COUNT + ALIGNED >R THEN ;
  746. : ALLOT ( n --- )
  747. \ Allot n extra bytes of memory, starting at HERE to the dictionary.
  748. DP +! ;
  749. : , ( x --- )
  750. \ Append cell x to the dictionary at HERE.
  751. HERE ! 1 CELLS ALLOT ;
  752. : C, ( n --- )
  753. \ Append character c to the dictionary at HERE.
  754. HERE C! 1 ALLOT ;
  755. : ALIGN ( --- )
  756. \ Add as many bytes to the dictionary as needed to align dictionary pointer.
  757. BEGIN HERE 03 AND WHILE 0 C, REPEAT ;
  758. : >NAME ( addr1 --- addr2 )
  759. \ Convert execution token addr1 (address of code) to address of name.
  760. BEGIN 1- DUP C@ 128 AND UNTIL ;
  761. : NAME> ( addr1 --- addr2 )
  762. \ Convert address of name to address of code.
  763. COUNT 31 AND + ALIGNED ;
  764. : HEADER ( --- )
  765. \ Create a header for a new definition without a code field.
  766. ALIGN
  767. CURRENT @ @ IF
  768. CURRENT @ @ HERE - , \ Create link field.
  769. ELSE
  770. 0 ,
  771. THEN
  772. HERE LAST ! \ Set LAST so definition can be linked by REVEAL
  773. 32 WORD DUP FIND IF ." Redefining: " HERE COUNT TYPE CR THEN DROP
  774. \ Give warning if existing word redefined.
  775. C@ 1+ HERE C@ 128 + HERE C! ALLOT ALIGN
  776. \ Allot the name and set bit 7 in length byte.
  777. ;
  778. : REVEAL ( --- )
  779. \ Add the last created definition to the CURRENT wordlist.
  780. LAST @ CURRENT @ ! ;
  781. : CREATE ( "ccc" --- )
  782. \ Create a definition that returns its parameter field address when
  783. \ executed. Storage can be added to it with ALLOT.
  784. HEADER REVEAL POSTPONE DOVAR ;
  785. : VARIABLE ( "ccc" --- )
  786. \ Create a variable where 1 cell can be stored. When executed it
  787. \ returns the address.
  788. CREATE 0 , ;
  789. : CONSTANT ( x "ccc" ---)
  790. \ Create a definition that returns x when executed.
  791. \ Definition contains lit & return in its code field.
  792. HEADER REVEAL 9 , , 5 , ;
  793. VARIABLE STATE ( --- a-addr)
  794. \ Variable that holds the compiler state, 0 is interpreting 1 is compiling.
  795. : ] ( --- )
  796. \ Start compilation mode.
  797. 1 STATE ! ;
  798. : [ ( --- )
  799. \ Leave compilation mode.
  800. 0 STATE ! ; IMMEDIATE
  801. : LITERAL ( n --- )
  802. \ Add a literal to the current definition.
  803. 9 , , ; IMMEDIATE
  804. : COMPILE, ( xt --- )
  805. \ Add the execution semantics of the definition xt to the current definition.
  806. DUP >NAME C@ 32 AND \ if primitive compile inline
  807. IF @ , ELSE HERE - CELL- , THEN
  808. ;
  809. VARIABLE CSP ( --- a-addr )
  810. \ This variable is used for stack checking between : and ;
  811. VARIABLE 'LEAVE ( --- a-addr)
  812. \ This variable is used for LEAVE address resolution.
  813. : !CSP ( --- )
  814. \ Store current stack pointer in CSP.
  815. SP@ CSP ! ;
  816. : ?CSP ( --- )
  817. \ Check that stack pointer is equal to value contained in CSP.
  818. SP@ CSP @ - ABORT" Incomplete control structure" ;
  819. : ; ( --- )
  820. \ Finish the current definition by adding a return to it, make it
  821. \ visible and leave compilation mode.
  822. 5 , [ ?CSP REVEAL ; IMMEDIATE
  823. : (POSTPONE) ( --- )
  824. \ Runtime for POSTPONE.
  825. \ has inline argument.
  826. R> DUP DUP @ + SWAP CELL+ >R
  827. DUP >NAME C@ 64 AND IF EXECUTE ELSE COMPILE, THEN
  828. ;
  829. : : ( "ccc" --- )
  830. \ Start a new definition, enter compilation mode.
  831. !CSP HEADER ] ;
  832. : BEGIN ( --- x )
  833. \ Start a BEGIN UNTIL or BEGIN WHILE REPEAT loop.
  834. HERE ; IMMEDIATE
  835. : UNTIL ( x --- )
  836. \ Form a loop with matching BEGIN.
  837. \ Runtime: A flag is take from the stack
  838. \ each time UNTIL is encountered and the loop iterates until it is nonzero.
  839. 17 , HERE - , ; IMMEDIATE
  840. : IF ( --- x)
  841. \ Start an IF THEN or IF ELSE THEN construction.
  842. \ Runtime: At IF a flag is taken from
  843. \ the stack and if it is true the part between IF and ELSE is executed,
  844. \ otherwise the part between ELSE and THEN. If there is no ELSE, the part
  845. \ between IF and THEN is executed only if flag is true.
  846. 17 , HERE 1 CELLS ALLOT ; IMMEDIATE
  847. : THEN ( x ---)
  848. \ End an IF THEN or IF ELSE THEN construction.
  849. HERE OVER - SWAP ! ; IMMEDIATE
  850. : ELSE ( x1 --- x2)
  851. \ part of IF ELSE THEN construction.
  852. 13 , HERE 1 CELLS ALLOT SWAP HERE OVER - SWAP ! ; IMMEDIATE
  853. : WHILE ( x1 --- x2 x1 )
  854. \ part of BEGIN WHILE REPEAT construction.
  855. \ Runtime: At WHILE a flag is taken from the stack. If it is false,
  856. \ the program jumps out of the loop, otherwise the part between WHILE
  857. \ and REPEAT is executed and the loop iterates to BEGIN.
  858. POSTPONE IF SWAP ; IMMEDIATE
  859. : REPEAT ( x1 x2 --- )
  860. \ part of BEGIN WHILE REPEAT construction.
  861. 13 , HERE - , HERE OVER - SWAP ! ; IMMEDIATE
  862. VARIABLE POCKET ( --- a-addr )
  863. \ Buffer for S" strings that are interpreted.
  864. 252 ALLOT-T
  865. : ' ( "ccc" --- xt)
  866. \ Find the word with name ccc and return its execution token.
  867. 32 WORD FIND 0= ABORT" Not found" ;
  868. : ['] ( "ccc" ---)
  869. \ Compile the execution token of the word with name ccc as a literal.
  870. ' LITERAL ; IMMEDIATE
  871. : CHAR ( "ccc" --- c)
  872. \ Return the first character of "ccc".
  873. BL WORD 1 + C@ ;
  874. : [CHAR] ( "ccc" --- )
  875. \ Compile the first character of "ccc" as a literal.
  876. CHAR LITERAL ; IMMEDIATE
  877. : DO ( --- x)
  878. \ Start a DO LOOP.
  879. \ Runtime: ( n1 n2 --- ) start a loop with initial count n2 and
  880. \ limit n1.
  881. POSTPONE (DO) 'LEAVE @ HERE 0 'LEAVE ! ; IMMEDIATE
  882. : ?DO ( --- x )
  883. \ Start a ?DO LOOP.
  884. \ Runtime: ( n1 n2 --- ) start a loop with initial count n2 and
  885. \ limit n1. Exit immediately if n1 = n2.
  886. POSTPONE (?DO) 'LEAVE @ HERE 'LEAVE ! 0 , HERE ; IMMEDIATE
  887. : LEAVE ( --- )
  888. \ Runtime: leave the matching DO LOOP immediately.
  889. \ All places where a leave address for the loop is needed are in a linked
  890. \ list, starting with 'LEAVE variable, the other links in the cells where
  891. \ the leave addresses will come.
  892. POSTPONE (LEAVE) HERE 'LEAVE @ , 'LEAVE ! ; IMMEDIATE
  893. : RESOLVE-LEAVE
  894. \ Resolve the references to the leave addresses of the loop.
  895. 'LEAVE @
  896. BEGIN DUP WHILE DUP @ HERE ROT ! REPEAT DROP ;
  897. : LOOP ( x --- )
  898. \ End a DO LOOP.
  899. \ Runtime: Add 1 to the count and if it is equal to the limit leave the loop.
  900. POSTPONE (LOOP) HERE - , RESOLVE-LEAVE 'LEAVE ! ; IMMEDIATE
  901. : +LOOP ( x --- )
  902. \ End a DO +LOOP
  903. \ Runtime: ( n ---) Add n to the count and exit if this crosses the
  904. \ boundary between limit-1 and limit.
  905. POSTPONE (+LOOP) HERE - , RESOLVE-LEAVE 'LEAVE ! ; IMMEDIATE
  906. : RECURSE ( --- )
  907. \ Compile a call to the current (not yet finished) definition.
  908. LAST @ NAME> COMPILE, ; IMMEDIATE
  909. : ." ( "ccc<quote>" --- )
  910. \ Parse a string delimited by " and compile the following runtime semantics.
  911. \ Runtime: type that string.
  912. POSTPONE (.") 34 WORD C@ 1+ ALLOT ALIGN ; IMMEDIATE
  913. : S" ( "ccc<quote>" --- )
  914. \ Parse a string delimited by " and compile the following runtime semantics.
  915. \ Runtime: ( --- c-addr u) Return start address and length of that string.
  916. STATE @ IF POSTPONE (S") 34 WORD C@ 1+ ALLOT ALIGN
  917. ELSE 34 WORD COUNT POCKET PLACE POCKET COUNT THEN ; IMMEDIATE
  918. : ABORT" ( "ccc<quote>" --- )
  919. \ Parse a string delimited by " and compile the following runtime semantics.
  920. \ Runtime: ( f --- ) if f is nonzero, print the string and abort program.
  921. POSTPONE (ABORT") 34 WORD C@ 1+ ALLOT ALIGN ; IMMEDIATE
  922. : ABORT ( --- )
  923. \ Abort unconditionally without a message.
  924. 1 ABORT" " ;
  925. : POSTPONE ( "ccc" --- )
  926. \ Parse the next word delimited by spaces and compile the following runtime.
  927. \ Runtime: depending on immediateness EXECUTE or compile the execution
  928. \ semantics of the parsed word.
  929. POSTPONE (POSTPONE) ' HERE - , ; IMMEDIATE
  930. : IMMEDIATE ( --- )
  931. \ Make last definition immediate, so that it will be executed even in
  932. \ compilation mode.
  933. LAST @ DUP C@ 64 OR SWAP C! ;
  934. : ( ( "ccc<rparen>" --- )
  935. \ Comment till next ).
  936. BEGIN
  937. 41 DUP PARSE + C@ = 0=
  938. \ [CHAR] ) DUP PARSE + C@ = 0=
  939. WHILE
  940. REFILL 0= IF EXIT THEN
  941. REPEAT
  942. ; IMMEDIATE
  943. : \
  944. \ Comment till end of line.
  945. SOURCE >IN ! DROP ; IMMEDIATE
  946. : >BODY ( xt --- a-addr)
  947. \ Convert execution token to parameter field address.
  948. CELL+ ;
  949. : (;CODE) ( --- )
  950. \ Runtime for DOES>, exit calling definition and make last defined word
  951. \ execute the calling definition after (;CODE)
  952. LAST @ NAME> R> OVER - -4 + SWAP ! ;
  953. : DOES> ( --- )
  954. \ Word that contains DOES> will change the behavior of the last created
  955. \ word such that it pushes its parameter field address onto the stack
  956. \ and then executes whatever comes after DOES>
  957. POSTPONE (;CODE) 69 , \ Compile the R> primitive, which is the first
  958. \ instruction that the defined word performs.
  959. ; IMMEDIATE
  960. \ PART 10: TOP LEVEL OF INTERPRETER
  961. : ?STACK ( ---)
  962. \ Check for stack over/underflow and abort with an error if needed.
  963. DEPTH DUP 0< SWAP 10000 > OR ABORT" Stack error" ;
  964. : INTERPRET ( ---)
  965. \ Interpret words from the current source until the input source is exhausted.
  966. BEGIN
  967. 32 WORD DUP C@
  968. WHILE
  969. FIND DUP
  970. IF
  971. -1 = STATE @ AND
  972. IF
  973. COMPILE,
  974. ELSE
  975. EXECUTE
  976. THEN
  977. ELSE DROP
  978. NUMBER? 0= ABORT" Undefined word" DROP
  979. STATE @ IF LITERAL THEN
  980. THEN ?STACK
  981. REPEAT DROP
  982. ;
  983. : EVALUATE ( c-addr u --- )
  984. \ Evaluate the string c-addr u as if it were typed on the terminal.
  985. SID @ >R SRC @ >R #SRC @ >R >IN @ >R
  986. #SRC ! SRC ! 0 >IN ! -1 SID ! INTERPRET
  987. R> >IN ! R> #SRC ! R> SRC ! R> SID ! ;
  988. VARIABLE INCLUDE-BUFFER ( --- a-addr)
  989. \ This is the buffer where the lines of included files are stored.
  990. 508 ALLOT-T
  991. VARIABLE INCLUDE-POINTER ( --- a-addr)
  992. \ This variable holds the address where the included line is stored.
  993. : INCLUDE-FILE ( fid --- )
  994. \ Read lines from the file identified by fid and interpret them.
  995. \ INCLUDE and EVALUATE nest in arbitrary order.
  996. INCLUDE-POINTER @ >R SID @ >R SRC @ >R #SRC @ >R >IN @ >R
  997. #SRC @ INCLUDE-POINTER +! INCLUDE-POINTER @ SRC !
  998. SID !
  999. BEGIN
  1000. REFILL
  1001. WHILE
  1002. INTERPRET
  1003. REPEAT
  1004. R> >IN ! R> #SRC ! R> SRC ! R> SID ! R> INCLUDE-POINTER !
  1005. ;
  1006. : INCLUDED ( c-addr u ---- )
  1007. \ Open the file with name c-addr u and interpret all lines contained in it.
  1008. R/O OPEN-FILE ABORT" Can't open include file"
  1009. DUP >R INCLUDE-FILE
  1010. R> CLOSE-FILE DROP
  1011. ;
  1012. : QUIT ( --- )
  1013. \ This word resets the return stack, resets the compiler state, the include
  1014. \ buffer and then it reads and interprets terminal input.
  1015. R0 @ RP! [
  1016. TIB SRC ! 0 SID !
  1017. INCLUDE-BUFFER INCLUDE-POINTER !
  1018. BEGIN
  1019. REFILL DROP INTERPRET STATE @ 0= IF ." OK" THEN CR
  1020. 0 UNTIL
  1021. ;
  1022. : WARM ( ---)
  1023. \ This word is called when an error occurs. Clears the stacks, sets
  1024. \ BASE to decimal, closes the files and resets the search order.
  1025. R0 @ RP! S0 @ SP! DECIMAL
  1026. 2 #ORDER !
  1027. FORTH-WORDLIST CONTEXT !
  1028. FORTH-WORDLIST CONTEXT CELL+ !
  1029. FORTH-WORDLIST CURRENT !
  1030. QUIT ;
  1031. : COLD ( --- )
  1032. \ The first word that is called at the start of Forth.
  1033. START ! START @ FORTH-WORDLIST +! START @ DP +!
  1034. SP@ S0 !
  1035. RP@ CELL+ R0 ! \ Initialize variables SO and RO
  1036. ." Welcome to Forth " CR
  1037. WARM ;
  1038. END-CROSS