First post for 8 bit week, I'll talk about a bug. An 8 bit bug.
I recently made a post about anonymous functions in NovaForth: you can surround some code with braces and it'll compile it and leave the address to it on the stack. For example: "{ 5 + }" will leave on the stack a pointer to some code that adds five to things. I have tests for all of this of course so I know it works.
Except it didn't: "execute" is a word that takes a pointer to a function and calls it. So, "{ 3 5 } execute" should be, create a function that pushes 3 and 5 to the stack, then call that function. But doing that in the NovaForth console ( http://sevendral.com/repl/ ) today did not work! It cleared the stack instead, which usually means that the thing crashed and called "quit". So, a conundrum: I know that brace functions work because they're tested all to hell, therefore the problem is in "execute," but "execute" isn't something that can really have a problem. Here's the entire source to that function:
jmp
A single jump with no arguments. Which is a single byte of code in Vulcan, byte 0x5c as a matter of fact. Like I said, it's an 8-bit bug. :) How can one byte have a bug in it?
The story here is that execute takes the pointer given it, calls it, and returns the result. But the pointer it's given is already on the stack, so we just need to call and return, and that's a tail call of course so we can just omit the extra stack frame and let what we're calling return for us, so, it's a jmp. Great, but, how can one instruction have a bug?
Well turns out it was a little bit trickier: I wanted brace functions to not take up space unnecessarily, so, if you make one from interpret mode it sticks it on the heap but doesn't increment the heap: as long as you're not compiling anything it'll be fine, and it means you can do things like "{ foo blah } 10 do-times" (if you were to write a word that takes a function pointer like that). Brace words written this way failed but brace words nested inside functions, even other brace words, worked great. More of a conundrum!
I'm proud that I figured this out without a debugger, although I do need to write a debugger: the answer was that more than just brace functions get built "temporarily" on the heap. As it turns out, a lot more: words being parsed use the heap as temporary storage, so my "execute" was getting written on top of the newly-compiler temporary function and then I was jumping into it. I've since fixed that but found a couple more things that cannot follow a brace function. I need to rearrange things to either use a separate internal scratch buffer for that stuff, or a separate internal scratch buffer for brace functions, I suppose...