mark4 changed the topic of #forth to: Forth Programming | do drop >in | logged by clog at http://bit.ly/91toWN backup at http://forthworks.com/forth/irc-logs/ | If you have two (or more) stacks and speak RPN then you're welcome here! | https://github.com/mark4th
<KipIngram> The logistic equation is a fun one to play with.
Lord_Nightmare has quit [Quit: ZNC - http://znc.in]
pbaille has joined #forth
dave0 has quit [Quit: dave's not here]
pbaille has quit [Ping timeout: 240 seconds]
Lord_Nightmare has joined #forth
Lord_Nightmare has quit [Quit: ZNC - http://znc.in]
LispSporks has joined #forth
pbaille has joined #forth
Lord_Nightmare has joined #forth
pbaille has quit [Ping timeout: 260 seconds]
pbaille has joined #forth
pbaille has quit [Ping timeout: 260 seconds]
Lord_Nightmare has quit [Quit: ZNC - http://znc.in]
Lord_Nightmare has joined #forth
sts-q has quit [Ping timeout: 240 seconds]
sts-q has joined #forth
LispSporks has quit [Ping timeout: 276 seconds]
Zarutian_HTC has quit [Read error: Connection reset by peer]
Zarutian_HTC has joined #forth
pbaille has joined #forth
pbaille has quit [Ping timeout: 240 seconds]
<KipIngram> You know, there's just not a particular clean way to make all the decisions that the outer interpreter needs to make, is there? We parse a word and search for it. We may succeed or fail. If we fail, we check to see if it's a number. If that fails it's an error so we're out of the building. But at this point we need to decide whether to execute or compile and treat it right whether it's a word or a number.
<KipIngram> I'm poking around at the control structures around that and they're really just not too tidy.
<KipIngram> It really looks like the execute / compile decision has to be made in two places (on the "word" path and the "number" path).
dave0 has joined #forth
<KipIngram> In most implementations I've seen EXECUTE is inside INTERPRET, at its level. Then the null word can just do a double return to break the infinite loop in INTERPRET. But if I factor that EXECUTE wants to move downward, and every time it does the null word has to add a level to how far it returns - it has to push execution back up and out of INTERPRET.
<KipIngram> Also there can't be any clutter on the stack during this time either.
<KipIngram> The number path is fairly clean.
pbaille has joined #forth
pbaille has quit [Ping timeout: 260 seconds]
<KipIngram> Ok, that's not so awful. I think this covers the bases:
<KipIngram> : interpret find check? 0=me execute me ;
<KipIngram> : check found? number? ;
<KipIngram> : found? .0=; run? .0!=;; swap compile ;;
<KipIngram> : run? state @ 0= imm? or ;
<KipIngram> : number? num state @ .0=;; literal ;
<KipIngram> : literal [compile] (lit) , 0 ;
<KipIngram> But I need to stare at it a while to be sure.
<KipIngram> At least it's "on the drawing board" now.
<KipIngram> Oh, didn't write imm? yet. It just grabs that flag, though.
Zarutian_HTC has quit [Ping timeout: 260 seconds]
<dave0> maw
<KipIngram> Hey dave0.
<dave0> hey KipIngram :-)
pbaille has joined #forth
Zarutian_HTC has joined #forth
pbaille has quit [Ping timeout: 240 seconds]
pbaille has joined #forth
<dave0> KipIngram: oops i only just saw your message
pbaille has quit [Ping timeout: 252 seconds]
gravicappa has joined #forth
pbaille has joined #forth
mtsd has joined #forth
pbaille has quit [Quit: Leaving...]
gravicappa has quit [Ping timeout: 252 seconds]
<KipIngram> I believe I can express a good "motivation" for giving conditional test words the ability to retain an argument. Sometimes you want to use a flag on the stack to optionally return from a word, but in the parent you use it again to optionally loop. So you "arg retain" on the first of those, then consume the flag on the second.
<KipIngram> : foo ... ... bar 0=me ... ;
<KipIngram> : bar ... .0=; ... ;
<KipIngram> I use a . prefix to indicate arg retention.
<KipIngram> All of the comparison-oriented words have it - if it's a word that would normally consume two args, ike > or =, then the . version consumes just the top one. If it's a word that would normally consume one arg, then the . version consumes none.
<KipIngram> It's particularly handy for comparing a data item to a series of values and acting in various ways based on which one it matches.
<KipIngram> In that case you don't need the data in the calling word - you just have a bunch of callee words that all may need the value.
<KipIngram> Combining that with double return makes it possible to sift through a series of tests, but abandon further testing when a comparison succeeds.
boru` has joined #forth
<KipIngram> : foo test1 test2 test3 failed-all ;
<KipIngram> : testi val .!=; process ;;
<KipIngram> That structure showed up in checking keyboard chars against control characters, and also for checking number chars against "special values."
<KipIngram> And a variation on it showed up when comparing number digits against the three ranges 0-9, A-Z a-z and computing an actual "digit value."
boru` is now known as boru
actuallybatman has quit [Ping timeout: 240 seconds]
gravicappa has joined #forth
<KipIngram> So, where do you guys check for stack under/overflow? Do you do that after the execution of each word, or at the end of a whole line?
<KipIngram> After each word seems better, but I'm not sure what the "custom" is.
<KipIngram> Each word *interpreted*, that is.
<KipIngram> So, in order for a simple ;; to make the null word do the right thing, not only does execute have to be at the level of the interpret loop - it also has to be a *primitive*. My execute currently is a primitive, so that works nicely.
gravicappa has quit [Ping timeout: 240 seconds]
<KipIngram> I just tried to wrapper it, though, so I could put the stack check right after the primitive, and of course that broke it - null no longer did the right thing.
<KipIngram> It would be easy to fix, but I kind of like how clean it is like this.
<KipIngram> The interpret word is really as long as I want it to get, though - I don't really want to add more in there. :-|
<KipIngram> This is what it looks like right now:
<KipIngram> : interpret find ?exec 0=me execute me [
<KipIngram> What's a better name for that word I've called ?exec ?
<KipIngram> It's doing a fair bit - it handles found words vs. numbers, and handles state and immediacy and so on.
<KipIngram> It basically handles everything except "execute NOW," which I get by having ?exec return a non-zero result.
<KipIngram> The logical place for ?stacks is right after execute.
<boru> Out of interest, what are you implementing this forth on?
<KipIngram> A Macbook Air, using nasm.
<KipIngram> Older one - not one of the new ARM machines.
<KipIngram> I could put the ?stacks word at the beginning of find. It would work, and the only word it would fail to check would be the null word, which doesn't do anything to the data stack. But it's just not the "happiest logical place" for it.
<boru> Ah, I was going to suggest using hw support for stack exceptions e.g. using the MPU to generate a permission fault on ARM, or similar mechanisms in other ISAs.
<KipIngram> Ah - I see. Well, yes - that's a possibility.
<KipIngram> I'd have to muck things around a bit, but in theory it should be easy to allocate some multiple of 4kB for the stack and surround it with non-writable memory regions.
<boru> It would just save you some donkey work, and be a bit more performant; ideally, you'd want to implement a mechanism to identify the fault site to the user, naturally.
<KipIngram> That's actually not a bad idea. It might not work on future ports, though.
<boru> Yeah, you're looking at reducing portability, or having to implement the mechanism for different ISAs.
<KipIngram> This check is only done when interpreting, though, so performance isn't that big a deal.
* boru nods.
<KipIngram> But thanks for putting the idea out there. :-)
<boru> Sure. It's just something I've utilised for my own implementations.
<KipIngram> I was just talking the other day about wanting at some point to figure out how to hook those errors anyway (invalid memory errors) and pass them through my error handling rather than just segfaulting.
<boru> I've got physical hardware support for soft stack machine I implemented, and am revisiting in a more recent project.
<KipIngram> I'm kind of hating on myself for even considering the idea of sticking ?stacks in find. It gets it executed at the right times and it doesn't make my interpret definition longer, but it just feels... "wrong" at the same time.
<boru> If you were using a process hypervisor, you could probably catch the signal in the child from the parent and possibly get a stack trace that way (since you're shooting from the hip trying to handle sigsegv in the child)
<KipIngram> Like the first thing someone would say if they looked is "Why is that THERE???"
<boru> Hypervisor being a parent which spawns child processes i.e. very lightweight.
<KipIngram> Right - that's an area I'd like to learn more about. I know what it is, basically, but have really no experience dealing with one.
<KipIngram> I think remexre has some interesting work in that area going on.
<boru> I did something sort of like that before, where the parent would fork/exec word chains without persistence (so the requirements were a little different than general purpose)
<KipIngram> He was itemizing his various layers the other day - one for each ARM security ring.
<boru> Yeah, I've seen/done similar things for RTOS I've seen/written.
<boru> Lots of ways to skin a variety of cats, and all that.
<KipIngram> The thing is my interpret definition isn't really THAT long. I've gotten rather badly poisoned over the last year or so about wanting super short definitions.
<KipIngram> "Factor factor factor" is good policy, but I guess anything can be taken too far.
<KipIngram> It used to be that if it would fit on a 64-char line I was happy, but I've gotten even more aggressive of late.
<KipIngram> I've been averaging probably 35-40 chars per def.
<boru> I'd say that's quite forthy, but yeah, I guess you don't want to end up with something that has the versatility of a transputer, with added overhead for each iteration...
<boru> I'd better get back to work before I need to abscond for lunch.
<KipIngram> Take care!
mtsd has quit [Remote host closed the connection]
<KipIngram> Oh man. I just had a horrible realization.
<KipIngram> I've fallen all in love with these conditional control words, and really went to town with them. The conditional return and double return words are no problem. But the condition loop words all require a jump offset following the word that actually checks the condition. That means that an immediate counterpart word will be required - for all of them - to properly compile them.
<KipIngram> Yuck.
<KipIngram> Ok, well, I punched all that in, and the state=0 path still works fine.
<KipIngram> I had it kludged up so I could test the number converter, but it's a "correct" structure now. Still missing a couple of bits in the state=1 path.
<KipIngram> I could try marking some words immediate and see if they executed right even with state=1.
gravicappa has joined #forth
dave0 has quit [Quit: dave's not here]
mtsd has joined #forth
<mark4> KipIngram, yes! IF ELSE etc are cool the way they are implemented
<mark4> look at the X4 sources, i have the ?branch words in the kernel but if/else/then etc are all in an extension :)
<mark4> >resolve <resolve >mark <mark seem confusing at first tho
<mark4> if is just a ?branch to an as yet unknown address. the else or then has to back patch the if :)
<KipIngram> Yeah - I'm accustomed to the general idea. It just hadn't "clicked" for me that that large number of conditional words were ALL going to need that treatment.
<KipIngram> I mean, it's obvious.
<KipIngram> I just hadn't consciously registered it.
<mark4> yup.
<mark4> also do loops etc
<mark4> begin is usually an alias for here, and again is an unconditional branch back to the address that here left on the stack
<KipIngram> Right. I actually don't have any other looping constructs other than these - I also don't have IF ... THEN. I've found the conditional returns and conditional recurse words adequate.
<cmtptr> i was going to suggest maybe a conditional tail recursion word instead of loops, but unless you have some way of determining the currently-executing word at runtime, that would also need an immediate implementation
<mark4> i would prefer for loops to conditional recursion personally :)
<mark4> with for loops i also implemented rep
<mark4> : blah .self ; \ emits its own name
<mark4> 10 rep blah blah blah blah blah blah blah blah blah ....
<cmtptr> yeah but recursion fits in better with his return-oriented programming vocabulary
<KipIngram> The conditional return words opened up a whole bunch of new structures for me.
<mark4> :)
<mark4> the beauty of forth is its implementor defined :)
<mark4> and it is still forth :)
<cmtptr> ugh, it is a workday today isn't it
<KipIngram> Conditional return takes the place of IF THEN. I just factor the body of the if then into its own word, with a reverse-condition conditional return at the front.
<mark4> not for me :(
<mark4> its dentist day for me later lol
<KipIngram> mark4: I agree. It is a beautiful thing.
<KipIngram> It is a workday for me, but my only meeting was cancelled and moved to tomorrow.
<mark4> YOU can implement a utterly CRAZY forth internally and... i will still be able to use it lol
<KipIngram> Well, I have a python script that generates the source for all these words. I can just have it generate the source for the immediate wrapper words too.
<KipIngram> It's easy enough - I just will get the PFA of the latest addition to the dictionary and that will be my target.
<mark4> is PFA the same as CFA ? i know CFA LFA NFA but i never understood PFA lol
<mark4> is the PFA the body of a variable? the part that contains the value?
<mark4> or the first xt of a colon def?
<KipIngram> PFA is just the address of the first cell of the definition.
<KipIngram> For : words.
<KipIngram> For variables it's just the variable address - where the data gos.
<KipIngram> You know, another way I could do this would be to just mark these words, and the compile code could check for that mark after compiling the primitive and recognize that it needs to add an offset to the dictionary.
<KipIngram> Then I wouldn't have to have all the extra words.
<KipIngram> I don't really have a good bit handy to do that with, though.
<mark4> thought i had a dentists appointment today at 3:30pm
<mark4> its TOMORROW and at 2:30 lol
<KipIngram> I could probably use the high bit of the second byte. I like that better than dozens of immediate words.
<KipIngram> The important thing is that the right primitive gets compiled, and that will happen automatically. I just need to know when to compile the distance back to the start of the definition.
<KipIngram> Or I could use the high bit of the 16-bit link field. I'm sure there are a bunch of bits in that field I will never need.
<KipIngram> I was just a little worried 256 bytes might not be enough, so I allocated two bytes for that.
<KipIngram> I rather like that. I could move all of the "meaningful" flags into the link field, and then use the high bit of the count byte to mark the beginning of the name. That would make it easier to implement nfa and lfa.
<KipIngram> I have my lfa just before the name instead of the usual just after.
<mark4> thats a good optimization i had considered
<mark4> i also have a pointer to the NFA one cell above the CFA
<mark4> so >name is cell- @
<KipIngram> Nice - that would be a little faster.
<mark4> instead of searching the dictionary for a word whose lfa points to the right cfa
<mark4> name> in my forth has to scan over the mae to the pointer
<KipIngram> I have [ backlink 2 bytes ] [ count byte ] [ name bytes ] [ 4-byte align ] [ cfa pointer ] [ pfa pointer ]
<mark4> if i changed it to lfa cfa-pointer "nfa" then both name> and >name would be cell- @
<KipIngram> last two are four bytes each.
<mark4> i was thinking of doing lfa-pointer, cfa-pointer, count, "name" align
<mark4> i never made taht change but its been on my todo for a while lol
<KipIngram> I've got a few things like that.
<KipIngram> Honestly, I need to do this. I don't actually have a really valid way to find the start of the name field from the CFA. I'd have to work my way back and identify the count byte, which involves recognizing the pad bytes as well. It's obtuse and I only THINK it's totally reliable.
<KipIngram> If I high-bit the first name byte, or the count byte, then I can just search back until I find a bit set.
<KipIngram> My assembler seems to pad with 0x90; it's a slight pain that that has the high bit set.
<KipIngram> I wonder if I can control that without writing an explicit macro for it.
<KipIngram> And I also wonder what made them choose 0x90.
<KipIngram> It will be easier to mark the count byte - it's treated separately in my symbol creation macro.
<KipIngram> AHA!
<KipIngram> You can specify that byte when you align.
<KipIngram> Just "align 4 <byte>" instead of "align 4."
<KipIngram> Nice.
<KipIngram> I see no reason not to use 0.
<KipIngram> No, it doesn't seem to like that.
<KipIngram> Oh, it's "align 4 db 0"
<KipIngram> Good.
<mark4> i think the align macro can specify the alignment byte
Zarutian_HTC has quit [Read error: No route to host]
<KipIngram> Yeah - I found it.
<KipIngram> align 4, db <byte>
<mark4> align macro might be used inside a code block so padding would have to be nops
<mark4> nop == 0x90
<KipIngram> Oh - of course.
<KipIngram> Well, I only changed it in my symbol macros.
Zarutian_HTC has joined #forth
tech_exorcist has joined #forth
<KipIngram> Ok, good. That's all done. Now I can use the count byte and an align to jump from the name to the CFA, and can scan back from the CFA looking for a set high bit, and that will be the count byte.
<KipIngram> Reserved the top three bits of the 16-bit back link for flags.
<mark4> or you could store a count byte at the end of the string too :)
<KipIngram> Yes, I could.
<KipIngram> So this limits me to 4kB backlinks, but that should always be plenty. I'd have to define a LOT of temporary words before unlinking them would exceed that.
<KipIngram> And I always *could* shift the back link over two bits, since it's always 4-byte aligned.
Zarutian_HTC has quit [Ping timeout: 265 seconds]
<KipIngram> Oh - or just use them as flag bits down where they are.
<KipIngram> That's better.
<KipIngram> Ok, good. IMMEDIATE is bit 0, TEMP is bit 1. Bit 15 is reserved for future use.
<KipIngram> So now the backlink can be up to 32kB.
<KipIngram> Oh, if I stored a count byte at the end it would need to go after the padding and point all the way back to the other count byte.
<KipIngram> Otherwise I'd still have to scan over the pad bytes to find it.
<KipIngram> I'm just going to scan back, though. Easy enough.
<KipIngram> OH! No, bit 15 isn't reserved. It's going to mark the words that need that jump back distance compiled after the primitive.
<KipIngram> That was the WHOLE POINT of what I was doing.
<KipIngram> Ok, good. All the conditional loop words now set that bit. Now I can just smarten up the compiler.
<KipIngram> I feel sure I can grab a couple more bits out of that field if I turn out needing anything.
<KipIngram> Ok, good.
<KipIngram> I think this means that I'll only have a small handful of immediate words.
<KipIngram> [ ; ;; [compile] and... I'm not sure yet what else.
<KipIngram> Oh, and : since I can define labels mid-word.
Zarutian_HTC has joined #forth
Zarutian_HTC has quit [Remote host closed the connection]
<mark4> [']
<mark4> " ['] ' literal ; immediate
actuallybatman has joined #forth
cantstanya has quit [Ping timeout: 240 seconds]
cantstanya has joined #forth
<KipIngram> Right - I'm sure I missed a handful.
<KipIngram> But now there will be 40 that I won't need to have. :-)
<KipIngram> 41 actually.
mark4 has quit [Quit: Leaving]
mark4 has joined #forth
<KipIngram> I wound up putting ?stacks inside "handle" here:
<KipIngram> : interpret find handle 0=me exec me [
<KipIngram> Somehow that didn't bother me as much as putting it in find.
<KipIngram> And because the last word processed on every line will be the null word, which has no stack effect, we do get an error check at the end of the whole line, effectively.
LispSporks has joined #forth
LispSporks has quit [Quit: My MacBook has gone to sleep. ZZZzzz…]
cp- has quit [Quit: Disappeared in a puff of smoke]
cp- has joined #forth
Zarutian_HTC has joined #forth
Zarutian_HTC1 has joined #forth
Zarutian_HTC has quit [Ping timeout: 240 seconds]
Zarutian_HTC1 has quit [Ping timeout: 240 seconds]
jess has joined #forth
cp- has quit [*.net *.split]
actuallybatman has quit [*.net *.split]
Monev has quit [*.net *.split]
rpcope has quit [*.net *.split]
spoofer has quit [*.net *.split]
hosewiejacke has quit [*.net *.split]
phadthai has quit [*.net *.split]
X-Scale has quit [*.net *.split]
Monev has joined #forth
cp- has joined #forth
X-Scale has joined #forth
rpcope has joined #forth
actuallybatman has joined #forth
spoofer has joined #forth
phadthai has joined #forth
hosewiejacke has joined #forth
cp- has quit [Max SendQ exceeded]
cp- has joined #forth
X-Scale has quit [Ping timeout: 252 seconds]
mark4 has quit [Ping timeout: 260 seconds]
Zarutian_HTC has joined #forth
X-Scale has joined #forth
gravicappa has quit [Ping timeout: 240 seconds]
mtsd has quit [Ping timeout: 240 seconds]
mark4_ has joined #forth
dave0 has joined #forth
<dave0> maw
mark4_ has quit [Remote host closed the connection]
mark4_ has joined #forth
mark4_ has quit [Ping timeout: 250 seconds]
<lispmacs[work]> we might be a small channel, but we've got at least twice as many logons as those ##fortran guys :D
tech_exorcist has quit [Quit: tech_exorcist]
<DKordic> lispmacs: http://krue.net/avrforth/ has a nice assembler . I would be happy to discuss any questions about AVR.
<lispmacs[work]> DKordic: i'm not familiar with that particular Forth. gforth alone compiles the flash image?
<lispmacs[work]> and is 328P supported?
Zarutian_HTC has quit [Ping timeout: 265 seconds]
<DKordic> Sorry for the delay. avrforth is hosted in gforth. It has assembler ""avr.f"", and a STC forth dialect implemented in avr.f.
<DKordic> Take a look at it's sources. Take what You like.