colorful rat Ratfactor.com > Dave's Repos

nasmjf

A NASM assembler port of JONESFORTH
git clone http://ratfactor.com/repos/nasmjf/nasmjf.git

nasmjf/devlog/log18.txt

Download raw file: devlog/log18.txt

1 I'm back! It's been over a month and I had to stop just 2 when it was getting exiciting. :-( 3 4 Anyway, I can now read any amount of jonesforth.f upon 5 starting the interpreter. So it's easy to test chunks 6 of Forth implementation by just incrementing the temporary 7 constant I've put in the assembly: 8 9 __lines_of_jf_to_read 10 11 But that doesn't mean it will be *easy*. The rest of 12 the FORTH implementation is implemented in FORTH, so 13 plenty of mind-bending lies ahead. 14 15 Setting the lines to read to 97, we now have these 16 10 new definitions to try out: 17 18 LITERAL ':' ';' '(' ')' '"' 'A' '0' '-' '.' 19 20 Yes, the quotes are part of the word names (it is 'A' not A). 21 22 First, LITERAL, sounds a lot like LIT, right? 23 24 Here's the entire definition: 25 26 \ LITERAL takes whatever is on the stack and compiles LIT <foo> 27 : LITERAL IMMEDIATE 28 ' LIT , \ compile LIT 29 , \ compile the literal itself (from the stack) 30 ; 31 32 I don't know ahout you, but I'm already starting to get a bit 33 rusty after my hitatus. So I'm going to need to remind myself 34 what these separate words mean. 35 36 IMMEDIATE Means LITERAL will always be executed as soon 37 as it's encountered even if we're in compile mode. 38 39 ' (TICK) Matches the next "word" of input with a compiled 40 word and returns its address. 41 42 LIT Pushes the next value onto the stack. 43 44 , (COMMA) Puts the currently-pushed value from the stack to 45 the position pointed to by HERE. 46 47 None of ' LIT , are immediate mode words, so they're being compiled 48 into LITERAL. 49 50 All together, this means that we 51 52 * get the address of the next word 53 * push that address on the stack 54 * write the address at HERE ("compile it") 55 * write the next address from the stack ("compile it") 56 57 Well, I can't make heads or tails of that functionality on its 58 own, but the next words make use of it. Jones thoroughly breaks 59 the first of them down in a big comment: 60 61 \ Now we can use [ and ] to insert literals which are calculated at 62 \ compile time. (Recall that [ and ] are the FORTH words which switch 63 \ into and out of immediate mode.) Within definitions, use [ ... ] 64 \ LITERAL anywhere that '...' is a constant expression which you would 65 \ rather only compute once (at compile time, rather than calculating it 66 \ each time your word runs). 67 : ':' 68 [ \ go into immediate mode (temporarily) 69 CHAR : \ push the number 58 (ASCII code of colon) on the parameter stack 70 ] \ go back to compile mode 71 LITERAL \ compile LIT 58 as the definition of ':' word 72 ; 73 74 Followed by the rest of the definitions: 75 76 \ A few more character constants defined the same way as above. 77 : ';' [ CHAR ; ] LITERAL ; 78 : '(' [ CHAR ( ] LITERAL ; 79 : ')' [ CHAR ) ] LITERAL ; 80 : '"' [ CHAR " ] LITERAL ; 81 : 'A' [ CHAR A ] LITERAL ; 82 : '0' [ CHAR 0 ] LITERAL ; 83 : '-' [ CHAR - ] LITERAL ; 84 : '.' [ CHAR . ] LITERAL ; 85 86 These are all crazy to look at coming from "normal" programming 87 languages because of the single quotes in the word names! 88 89 Anyway, taken all together, we get: 90 91 * Compile a word called ':' 92 * Switch to immediate mode 93 * CHAR takes the next character and puts on stack 94 * : is next character 95 * Switch back to compile mode, : is on stack 96 * LITERAL: 97 * gets the address of the next word 98 * push that address on the stack 99 * write the address at HERE ("compile it") 100 * write the next address from the stack ("compile it") 101 102 Or, more simply: we put the ASCII value of the character on the 103 stack, and LITERAL compiles the LIT word followed by the character 104 from the stack. Thus, we end up with a compiled word that will 105 perform a LIT + character (puts that character on the stack). 106 107 Okay, got it. Let's see it in action with one of the defined words: 108 109 'A' . 110 65 111 'A' EMIT 112 A 113 ':' EMIT 114 : 115 116 Now I'll try making my own word that uses LITERAL: 117 118 : Z [ CHAR z LITERAL ] ; 119 Z EMIT 120 z 121 122 And I can compile that into another word: 123 124 : emitz Z EMIT ; 125 emitz 126 z 127 128 Yay, I finally understand this! I kept trying to "get it" late 129 at night and it just wasn't clicking. What made this so hard 130 to understand has nothing to do with the stack, it's the 131 temporal nature of the compile-time vs run-time execution of 132 these low-level compiler words in FORTH. 133 134 Some of it runs "now" and some of it runs "later". For certain 135 definitions of now and later. Thankfully, it's all much 136 more comprehensible on a weekend morning. 137 138 Well, this log has now spanned two months. So even though I 139 didn't cover a TON of ground here, I'm gonna start a new log 140 for the next installment. :-)