2023-08-27 05:20:47 i'm writing a little Forth interpreter for learning, one question: the eval() function which will evaluate Forth code, will return something? a type of the system? e.g. a integer/string or whatever or just a control code to ok/error ? 2023-08-27 05:30:06 rendar: this is an implementation detail of your forth system so do whatever you like 2023-08-27 05:30:25 ok, but what Forth systems do usually? 2023-08-27 05:30:32 they usually do not provide an API 2023-08-27 05:31:21 I guess a good fit would be giving the eval() function an initial stack state as an argument. The Forth code modifies this stack. No return value, but the caller can inspect the stack to see what the Forth code did. 2023-08-27 05:31:52 e.g. if you wanted to square a number in Forth, you would do something like 2023-08-27 05:32:08 stack.push(42); eval(stack, "dup *"); square = stack.pop() 2023-08-27 05:42:57 i see 2023-08-27 05:43:00 that's right 2023-08-27 05:48:52 rendar: Usually the "eval" loop in Forth doesn't return anything on the stack in its own right. Basically it's job is to parse words one at a time out of the input stream, find them in the dictionary, and execute them (or add their address to an ongoing new definition, but that's when you're compiling). 2023-08-27 05:49:16 right, i agree 2023-08-27 05:49:24 if i want the result, i just pop it from the stack 2023-08-27 05:49:25 When it executes those words, *they* have whatever effect on the stack they're designed to have. The eval loop itself can't intermix its own stuff with those items. 2023-08-27 05:49:45 The eval loop doesn't know what the executed words are going to do to the stack, so the stack isn't a safe resource for it to use. 2023-08-27 05:50:20 And supposedly the user is interpreting those particular words for their stack effects - the eval loop can't interfere with that. 2023-08-27 05:51:04 And those words aren't guaranteed to put something on the stack - they might remove things or rearrange things are whatever. 2023-08-27 05:51:58 s/are/or/ 2023-08-27 05:52:42 When you are compiling rather than interpreting, the compiler loop has the opportunity to use the stack itself, and does so to manage its compiliation process. 2023-08-27 05:53:13 For example, IF will usually leave an address on the stack and then THEN later uses that address to backpatch the forward jump target that IF has compiled the conditional jump for. 2023-08-27 05:53:36 i see 2023-08-27 05:53:53 There are numerous cases where the compiler loop uses the stack. Since you are compiling, you aren't executing, so the user's specified words are meant to have an affect later, when the new word is executed, rather than "now." 2023-08-27 05:54:21 So the compiler is free to make some assumptions that the stack is NOT underoing constant unknown modifications, and therefore it can use it in certain ways. 2023-08-27 05:54:33 effect 2023-08-27 05:54:38 yes, i see 2023-08-27 05:59:14 the thing is: `eval("23 .")` doesn't return 23? i mean, it pops the latest value from the stack, then what it does with it? print to the screen? eval display directly to the screen? 2023-08-27 06:18:42 rendar: yes, . (dot) pops the stack and prints the number ... if you want to print it and keep it, dup it 2023-08-27 06:19:07 rendar: 23 dup . 2023-08-27 06:23:18 ok but internally, what is the concept of printing? writing something into some buffer? 2023-08-27 06:24:08 rendar: implementation defined 2023-08-27 06:24:13 rendar: you can print with EMIT and TYPE 2023-08-27 06:24:35 rendar: in a hosted implementation, the EMIT primitive would call the underlying system call 2023-08-27 06:24:48 on a freestanding implementation, EMIT interacts with some peripheral to print text 2023-08-27 06:25:40 if you were using a microcontroller, EMIT might write a character to a serial port 2023-08-27 06:28:14 rendar: you can certainly write a number into memory.. forth provides <# # # #> for that 2023-08-27 06:28:44 rendar: i mean format it in a way that a person could read it 2023-08-27 06:29:00 i see 2023-08-27 06:29:35 but what you mean with `<# # # #>` exactly?! 2023-08-27 06:30:49 rendar: they are forth words.. <# prepares the buffer # puts a digit in the buffer (you can have more than one #) and #> finishes and leaves a pointer on the stack, which you can TYPE so the user can see 2023-08-27 06:31:04 that's pretty much how dot works 2023-08-27 06:31:30 i see 2023-08-27 06:33:48 rendar: try this 345. <# # # # # # #> type 2023-08-27 06:34:17 the . there makes it a double word (the #'s work on a double) 2023-08-27 06:34:20 00345 ok 2023-08-27 06:34:40 see how a # cooresponds to a digit? 2023-08-27 06:34:44 yes 2023-08-27 06:38:37 rendar: the "printing" just refers to some arbitrary "sink" you can send characters into. You find a lot of variation in how various Forths do these things, but in the simplest model you have KEY, which somehow someway gets a character code onto the stack, and EMIT, which takes a character code from the stack and "does something" with it. The details don't really matter. You can then construct everything 2023-08-27 06:38:39 else on top of KEY and EMIT. 2023-08-27 06:38:52 Thinking of them as arbitrary buffers of some sort works fine. 2023-08-27 06:39:12 ok i see 2023-08-27 06:39:15 They're really just data motion - "from somewhere to stack" and "from stack to somewhere." 2023-08-27 06:39:28 clear 2023-08-27 06:39:34 In some systems those are made vectorable so you can aim them various places. 2023-08-27 06:39:55 what about you have a very big item? e.g. a very long string? you will fill all the buffers there.. 2023-08-27 06:41:24 <# prepares an empty setup for writing out a number. # pulls one digit from the number on the stack and adds it to that buffer. #> 'finishes up'. Again, the details can varry. The idea is just to give you a way to take other actions in various places, to put things like dollar signs, commas, decimals, and so on in the right places in a formatted number. 2023-08-27 06:41:40 Whereas . just does the whole job with no special formatting other than a trailing space (usually). 2023-08-27 06:42:17 A given Forth implementation doesn't promise to be immune to all problems arising from, say, limited buffer sizes and so on. 2023-08-27 06:42:28 You just have to know what your system is capable of and not exceed those limits. 2023-08-27 06:42:38 rendar: they usually point to a keyboard and screen of some sort .. keyboards and screens are sort of "infinite" you can always pretty another button, or have the text scroll off the top of the scren 2023-08-27 06:42:41 There's a real hardware world out there that you're supposed to be familiar with. 2023-08-27 06:42:51 press another button* 2023-08-27 06:43:14 ok, right 2023-08-27 06:43:58 Often Forth does its disk interface using 'blocks'. That's an adjustment for some people who are used to files. You get in the habit of treating files like they're infinite, but blocks have a fixed size, like 1kB or 4kB and the Forth programmer has to behave accordingly. 2023-08-27 06:44:44 Obviously a C programmer knows that his disk isn't infinite, but usually it's "infinite for all practical purposes." 2023-08-27 06:44:53 Big enough for you not to worry about it. 2023-08-27 06:45:58 Most Forths have a data area called TIB (text input buffer). That's where each line you type at the console gets put for parsing. That generally has some limtied size. It might be 256 bytes, for example. 2023-08-27 06:46:14 You can't exceed that, but most of the time you won't want to type lines that long. 2023-08-27 06:49:24 a man has got to know his limitations! 2023-08-27 06:49:41 I guess as a hardware guy Forth came pretty easy to me - I'm in a good habit of recognizing that I've got some limited set of resources out there. 2023-08-27 06:49:45 ^^ Right, dave0. 2023-08-27 06:49:58 The Zen of Clint. 2023-08-27 06:50:50 lol 2023-08-27 06:51:07 love that guy! 2023-08-27 06:51:10 Rather the Tao of Clint - that's what I was really trying to bring to mind. 2023-08-27 06:51:37 :-) 2023-08-27 06:52:04 Computer science paints an almost mathematically precise picture of the world, and sometimes obfuscates those always there boundaries and limitations. 2023-08-27 06:52:14 Forth has grown from dirtier roots than that. 2023-08-27 06:53:24 Even a $200 computer can often put those boundaries so far away you can ignore them, but get on some little embedded gadget and suddenly the walls close in tight around you. 2023-08-27 07:49:57 Heh. Got the Python compiling source code now; it handles var and : items, growing the dictionary accordingly. 2023-08-27 07:50:44 I want to have .: as well, though, which I'll initially do by having the Python keep a side structure for those names. 2023-08-27 07:51:11 right now it only compiles vm instructions - doesn't know how to search the dictionary yet. 2023-08-27 07:55:13 I've taken my source block and split it on spaces, giving me an array of words. Each one I process I get using words.pop(0), removing it from the head of the list. So, whenever I've got enough in the dictionary for it to take over the parsing job, I can just start feeding subsequent words into the emulation RAM and letting the Forth take it from there. 2023-08-27 08:05:30 Oh, hmmm. Minor issue I'll have to fix. The other day I wrote a function compile_val(op, val). It's intended, for example, to compile a call operation followed by the target offset. It will check to see if that will fit in what's left of the current cell, and if not will new_cell(), where we'll have more room. But in the case of a call, the val would need to change if we move to a new cell, and that's 2023-08-27 08:05:32 not how it's currently written. 2023-08-27 08:07:40 Easy to fix though - I think I can just adjust val by four if I move a cell. 2023-08-27 10:40:34 Well, this all seems to be working. 2023-08-27 10:44:34 Does tail optimization too. Every time it compiles a call, it sets a flag and marks where the opcode went in RAM. Everything other than a ; clears that flag, and ; checks it - if it's set it flips a bit in that compiled opcode - otherwise it compiles a return. 2023-08-27 11:26:34 I think it's ready for me to start writing system code now. I'm sure I'll see some little things that need adjusting as I go along. Probably ought to write a decompiler that dumps the dictionary. Just to make it easier to keep an eye on things. 2023-08-27 18:07:13 So, around the time he did the F18A Chuck advocated A and B address registers, and his @ fetched using the address in A. The word A! stored from the data stack into A. So where you'd normally have said @, you use A! @ instead. 2023-08-27 18:07:37 I wonder if in such a system executing a variable name perhaps should LOAD A with that variable's address rather than leave it on the stack? 2023-08-27 18:08:21 Same for ! of course. Having that variable behavior seems like a natural complement to the operation of @ and !. 2023-08-27 18:09:33 A has postincrement options as well (@+, !+) so that whole little arrangement sets you up well for working with arrays and strings and so on. 2023-08-27 18:10:52 This also allows more of a differentiation between variables and constants. In most systems a constant is just a variable with an automatic @ attached. Here, though, constants wouldn't touch the A reg. No need for that - the address involved would be hidden under the hood. 2023-08-27 19:10:20 Oh. I need to modify my header function so that headers in the middle of definitions get jumped around. 2023-08-27 20:23:40 ACTION pines for indirect JSRs for WDC 6502 2023-08-27 20:24:14 I would pine for that too since only the 65816 has indirect JSR :P 2023-08-27 20:26:12 but the Western Design Center version has an absolute indirect JMP so I can fake it 2023-08-27 20:27:07 of 6502 that is 2023-08-27 21:29:59 You know, thing is working pretty well. Every jump or call offset I check, in all of these weird situations like jumping around headers and so on, seem to be right. 2023-08-27 21:30:31 I do see some rough edges, but given that I just started working on this part a day or so ago, I'm pretty happy. 2023-08-27 21:32:17 you could also pine for a pine64 (wackily, the japanese word for pine also means pine or to pine for) 2023-08-27 21:33:16 Oh, that's an interesting coincidence. You mean the tree names mean "pine as in long for" in both languages too? 2023-08-27 21:33:23 Same "dual usage"? 2023-08-27 21:34:41 yep 2023-08-27 21:35:11 That's cool. 2023-08-27 21:38:21 I think all of these "rough edges" arise from the lack of enough flag bits in the short headers. I think the way out of that, though, is to not have ANY count field in a short header. Short header words can be up to 3 chars long, and I think I'll fix this by just "not counting" zero pad chars. In other words, the count will be implicit in the name chars. 2023-08-27 21:38:44 then I don't need any bits of the first byte for length, and I can use those two bits for flags instead. 2023-08-27 21:39:20 I've got four flags - INLINE, SHORT_HEADER, IMMEDIATE, and NEEDS_OFFSET. 2023-08-27 21:39:48 So short headers will have four link bits and four flag bits. 2023-08-27 21:41:17 Think I'll let those thoughts percolate until tomorrow and fix it then. 2023-08-27 21:42:52 Zarutian_iPad: the non WDC ones have that too :)