00:00:00 --- log: started forth/21.04.01 00:42:57 --- join: xek joined #forth 01:35:39 --- quit: f-a (Quit: leaving) 02:25:53 --- join: tech_exorcist joined #forth 02:31:00 --- join: f-a joined #forth 02:43:41 --- quit: f-a (Remote host closed the connection) 03:00:06 --- join: f-a joined #forth 03:42:13 * KipIngram stumbles blindly toward the coffee smell... 06:21:40 --- quit: f-a (Quit: leaving) 07:00:15 --- join: f-a joined #forth 07:34:44 --- quit: dave0 (Quit: dave's not here) 08:32:21 --- quit: lispmacs (Remote host closed the connection) 10:23:19 --- join: Zarutian_HTC joined #forth 10:28:33 --- quit: f-a (Ping timeout: 240 seconds) 10:30:18 --- join: f-a joined #forth 11:43:33 --- quit: gravicappa (Ping timeout: 240 seconds) 11:45:24 --- join: gravicappa joined #forth 12:34:48 --- join: tech_exorcist_ joined #forth 12:35:17 --- quit: tech_exorcist (Ping timeout: 265 seconds) 12:40:07 --- quit: tech_exorcist_ (Remote host closed the connection) 12:40:20 --- join: tech_exorcist joined #forth 13:23:36 --- quit: lispmacs[work] (Remote host closed the connection) 13:23:56 --- quit: gravicappa (Ping timeout: 268 seconds) 13:35:06 --- quit: f-a (Quit: leaving) 14:44:20 --- quit: tech_exorcist (Ping timeout: 265 seconds) 14:47:15 --- join: tech_exorcist joined #forth 15:13:49 --- quit: tech_exorcist (Ping timeout: 265 seconds) 15:26:24 --- join: lispmacs[work] joined #forth 15:49:23 I wonder what's the minimum number of words one would have to implement in host code for a Forth implementation where all other words can be built using those primitives. 16:06:13 neuro_sys: ya mean the primitives? I gotten them down to sixteen 16:07:34 Zarutian_HTC: Yes, ah that's very cool. 16:12:43 lesse, nop um+ and xor LeftBitRotate 1+ @ ! dup drop swap SKip-if-Zero >R R> ext exit 16:14:34 for the vm I am using if the three first nybbles in a 16 bit cell are zero then it is an primitive otherwise it is a call to that address 16:17:14 I usually either have io memory address mapped or use the ext primitive to put in key? and emitt 16:18:05 (an order code on top of stack selects which) 16:21:35 --- join: dave0 joined #forth 16:22:51 maw 16:39:11 --- quit: Zarutian_HTC (Read error: Connection reset by peer) 16:39:27 --- join: Zarutian_HTC joined #forth 17:04:17 --- join: eli_oat joined #forth 17:19:40 --- quit: eli_oat (Quit: WeeChat 2.8) 18:10:02 N 18:10:57 neuro_sys: I build up from 30 primitives, but could drop some of them if I needed to 18:13:27 15-18 would probably be my minimal set 18:13:47 * crc makes a note to test this sometime 18:44:30 --- join: boru` joined #forth 18:44:33 --- quit: boru (Disconnected by services) 18:44:35 --- nick: boru` -> boru 18:57:36 what is the point of using so few primitives, unless one is targeting something like an FPGA? 18:58:11 the reason why I ask is that even on very small systems, primitives hardcoded in assembly are likely to be denser than code generated by the forth compiler 19:27:12 I run on an emulated MISC architecture; my primitives correspond to the instruction set 19:27:57 I use inline assembly for some definitions, with the non-assembly versions as comments 19:28:52 I just prefer to keep the assembly part as small as possible as it's less readable than Forth 19:31:29 tabemann: virtual machine implemented as an boolean sequential logic runnable ontop of smpc 19:32:39 basically implementing a variation of the canonical dual stack machine from Philip Koopmans book Stack Machines the new wave 19:34:10 and sometimes as a simple while switch construct in c, python, tcl, lua, javascript, and bash 19:35:18 I've implemented a very large set of primitives for zeptoforth because these primitives are denser than code generated by the code generator, and they can be copied verbatim into generated Forth code by the inliner 19:36:09 of course, if I wanted to save space, I'd use a VM with 256 primitives 19:36:20 running on top of the Cortex-M MCU 19:36:26 but that'd be slow 19:36:43 and I've specifically chosen to sacrifice space for speed 19:38:12 I can imagine the slowdown on something like pipelined arm if you used fewer primitives 19:41:37 but yeah, if I wanted to write a really small forth I'd use token-threading, as then you can get 8 bits per primitive, which is way denser than what my native code generator/inliner can do 19:45:07 I take it you would write it is assembly targetting some existing arch? 19:46:54 yes 19:47:00 yeah, I needed some simple to implement arch that has the same code density as Forth, hence the few primitives and canonical dual stackmachine 19:48:06 to me the reason to use more, not less primitives, is that primitives can be really cheap if you do them right (e.g. they can take up one byte, so one gets 256 of them) 19:48:22 and I mean implement as in specifying it in a bit extended BLIF or seq logic 19:49:29 the only reason I see to use fewer primitives is if A) one does not have enough space to implement them all or B) one is targeting something like an FPGA where implementing all that logic would be difficult 19:50:38 precisely that I do not have the space to implement them 19:51:33 space in the sense of number of and gates and such 20:00:38 FPGA's are the key area to me where it seems like minimizing the number of primitives would make sense 20:01:15 whereas on a MCU or like LIT 1 + will always take up more space than a 1+ primitive 20:03:32 and if you can have a good number of primitives one can have a number of then like LIT_-8, LIT_-4, LIT_-2, LIT_-1, LIT_0, LIT_1, LIT_2, LIT_4, LIT_8, and like 20:03:53 where the primitives encode small constants so a separate literal field is not needed 20:03:56 I do have an 1+ primitive but mainly because it is easier to implement (LIT) as : (LIT) R> DUP 1+ >R @ ; 20:04:40 (cell addressed memory, not byte addressed) 20:06:17 and I define early such LIT words as you listed 20:07:26 mainly because then code that use those literals takes up less memory space 20:07:46 exactly 20:08:51 --- join: gravicappa joined #forth 20:11:49 okay, g'night 20:11:58 gn 21:10:02 --- quit: xek (Remote host closed the connection) 21:23:19 1+ almost certainly should be a primitive - it usually requires just one machine instruction, and then you go straigt to the inner interpreter. 21:42:13 neuro_sys: How few primitives you can get to depends on what your goal is. If you want the absolute minimum count no hold barred, then something in the 8-16 range is pretty easy to reach. But if you want to be able to say your system is anywhere near best performance you need more. Because if you have that minimum count the diddling around you have to do to implement things that could be extremely efficient 21:42:15 primitives leaves you with a slow systen. 21:44:30 I'm splitting that whole business into two layers in my latest system. I'm creating a set of assembly macros that I call "virtual instructions." And they are macros - a sequence of them generates consecutive code sequences. Then write primitives using those. And my general goal is to be able to have an implementation that will map out to optimum, or at least almost optimum, performance primitives on both 21:44:32 x64 and ARM instruction sets. 21:44:50 I'm guessing at this point, but I suspect I'll wind up with 50-60 virtual instructions. 21:45:39 My system is heavy on primitives, because I have a lot of conditional return and conditional jump primitives. But only the virtual instructions will have to be re-done for a platform port. 21:47:08 --- quit: Zarutian_HTC (Read error: Connection reset by peer) 21:47:14 --- join: Zarutian_HTC joined #forth 21:48:19 You could imagine that instead of making those macros I made them themselves the primitives - then a lot of the things I'm calling primitives would be written threaded. I just push the "threading overhead" down one layer by inlining the virtual instructions. 21:49:50 Most of the vi's are one-liners on x64, but the same vi might become a two-liner or something on ARM, since a lot of ARM instructions are register only. 21:50:42 But the whole purpose was to get a "significant reduction" in the volume of things that need re-implementation on a port. From hundres of primitives down to dozens of vi's. 21:53:14 So I have conditional return primitives, conditional two-level return primitives, and conditional "recurse" words (that mens jump to the beginning of the definition). 21:54:20 And I also have variants of those that will retain one stack item more than the normal operation would. I give those a . prefix. So whereas = has two stack arguments and consumes them both, .= only consumes the one that' on TOS. 21:55:16 So once you itemize that all out as a complete family it's a slew of primitves. But these things have gradually become my flow control words of choice. 21:55:52 I code in a completely different way from how I did before implementing this idea. 23:25:06 --- join: f-a joined #forth 23:59:59 --- log: ended forth/21.04.01