Submissions by Harley tagged forth

Back at it

A submission for Code @Home 225

I'm not going to attempt every day, but I'll try to post here a bit again. I've more-or-less been lazy about submitting here. In any case, maybe this will motivate others to remember to submit again.

Mostly I've worked on GoForth the last couple of months. It's been pretty fun hitting 100% test coverage and benchmarking most implemented words. As an example:

BenchmarkCoreWordsStackSwapCodeWord-2            2000000               638 ns/op
BenchmarkCoreWordsStackSwapFastCodeWord-2        5000000               339 ns/op

BenchmarkCoreWordsStackRotCodeWord-2             2000000               837 ns/op
BenchmarkCoreWordsStackRotFastCodeWord-2         5000000               270 ns/op 

This shows a stack swap time performing 1.8x as fast and a stack rotation performing 3.1x as fast. This one speedup was taken across any operation that was popping from the stack and then pushing a result. In most cases, an item is popped if needed, and then the top item is changed in place (or in the case of unary operators, changed in place without any push/pop.)

I also now have a basic REPL. It was surprisingly short:

package main

import (
    "fmt"

    "forth"
)

func main() {
    reader := make(chan rune)
    quit := make(chan bool)
    f := forth.NewForth()
    f.SetStdInOut()
    f.Reader(reader, quit)

    fmt.Println("Welcome to an example REPL.\nType die and press enter to exit")
    var s string
    for {
        fmt.Scan(&s)

        // Just a simple break out of the repl
        if s == "die" {
            break
        }

        for _, v := range s {
            reader <- v
        }

        reader <- ' '
    }
}

This will read from input, execute any words in the dictionary, and exit when you type " die" (without quotes and the leading space) and press enter.

Next up will likely be things like branching words and figuring out a good method for doing that.

Took another break mostly out of lack of time, other obligations, etc. I got back into it a bit tonight when I realized I hadn't updated Docker in a very long time (as in, I was on version 0.4.6 circa June 22, 2013) on my desktop at home. Upon updating that, also built the docker client for the BeagleBone Black.Unfortunately (and rather expectedly), the daemon wouldn't build.

After playing around with that a bit, I started prototyping a Forth implementation in Go. I got as far as defining a couple structures implementing a common word header (name & flags mostly.) They also satisfy a common interface which includes a codeword function. The linked version iterates through the functions executing them, while the func struct runs a single function defined in the structure. This seems like a pretty straight forward way to implement this, but I'll likely play around with other methods out of curiosity.

Needed to take a bit of a break after the 33 day streak. Spent a bit of time looking at the Forth word structure. Figured out .int was giving a 32 bit int instead of 16 bit. Think the alignment issues should be worked out now. Did some other testing with MSP430 Forth. Simplified the test case a bit by about 10-20 lines.

Played around with the minimal Forth I pulled out and think I've figure out why IP was going to 0. So I pulled in more words to have a closer to realistic test (QUIT, RZ, RSPSTORE, & BRANCH.)

Spent my time tonight ripping out everything but what was needed to test some things. Specifically it still had NEXT, DOCOL, main, a word, and an assembly code word. I believe I've tracked the problems I've been having to the way the instruction pointer is handled in the inner interpreter. I'll probably spend tomorrow diagramming out the memory & program flow. It's nice to have the problem narrowed down more. It's much less nice not knowing exactly how to solve said issue.

I spent a fair amount of time moving code out of the main (only) source file into smaller files. I first pulled the macros into a header, then I pulled groups of word definitions into separate files, and finally fixed compilation errors. I ended up needing to declare more labels as global. In addition to that, I also implemented the rest of the comparison operations, fixed some potential problems, and finished finished the implementation for another word. I think I'm down to the last 9 words to implement (and some have partial implementations.)

Went through and added TODO markers for all incomplete words. Made stubs for several subroutines. Implemented MUL & DIVMOD as well as untested >CFA & INTERPRET.

I think I mentioned this before, but the one thing I dislike about MSP430 assembly is the lack of multiplication & division mnenomics. That said, the naive multiply function is trivial:

/* Arguments are passed in r12 & r13, r15 is used to store the result */
    clr r15
1:  add r12, r15   /* ignores overflow */
    dec r13
    jnz 1b
/* To comply with the MSP430 GCC calling convension, the result would be put back into r12 */

Division is a bit more complicated, but still didn't seem too bad. I'm not posting that because I haven't done any verification of it yet. This one at least had minimal testing in isolation.

mul.S187 bytes
2 downloads

Quick

A submission for Code @Home 30

Keeping it short since it's really late. Did a bit of Ember.js with Go and finally understand how Ember templates have to be included since there isn't an asset pipeline that embeds the Handlebars templates into the file. I could add that to pipeln, but that means I'll need to get more into filters to allow it to be extensible.

I then spent a fair amount of time with MSP430 Forth running it on an MSP430G2553 setting breakpoints and single stepping. Things seem to work up to a point, and I figured out where it's failing. However, I'm not 100% sure what needs to happen to fix it. Need to sleep on it.

Only had about 15 minutes today. Spent it looking at msp430-forth and started implementing KEY when I ran out of time. Maybe I'll have more time tomorrow.

Implemented around 9 more words for this Forth implementation. Again, I mostly focused on easy words, but that's about the last day I can do that. The rest are a bit more complicated, but are the last bits for really getting a working Forth. The other outstanding piece is serial output so things like TELL, EMIT, & KEY can be implemented.

It was getting kind of late, and I decided to go back to my MSP430 port of jonesforth that I've been on for awhile now. I implement a few more words, improved readability of the defvar & defconst statements, and fixed a potential issue with alignment. I have about 20 words left to implement of the assembly before I can start really testing it. Really, it's getting about to the point where I'll have to implement a basic serial interface for interaction. I'm currently at 2602 bytes, still well within the MSP430G2553's 16K of storage. However, I'm clearly above some of the smaller chips, like the MSP430G2211 which was one of the original chips to ship with the Launchpad. I'm not sure how much I really care to target those devices.

I spend a fair amount of time tonight looking around at other VM's. The main blocking point(s) I've run into (more than once) with this stack based VM is how it talks to the outside world, to compile or interpret, to implement a Forth on the VM capable of interpreting for itself, C access, and native vs codewords. The only thing I really coded was to rename a define.

Missed a day :(

Did some cleanups and additions to a stack based VM I wrote a while back in C. One cleanup was to pull the frequent underflow checks into a C macro. I added some VM stack ops dealing with stack manipulation that corelate directly to the Forth words: ?DUP, OVER, TUCK, & NIP. I also simplified my implementation of ROT & -ROT to just do the top three items instead of taking a top item and doing an arbitrary rotate.

Overall: 7 files changed, 124 insertions(+), 137 deletions(-)

I'm going to cheat a little because I didn't get home from work until about 20 minutes ago. This is a place-holder until I have a bit of time to write about something. I'll likely be doing a bit of MSP430 assembly again tonight since I have my MSP-EXPFR5969's firmware up-to-date now.

Ok, I really did end up doing a bit of MSP430 assembly. Energia on Linux was still complaining about the programmer firmware still being out of date, so I went through and more thoroughly tried updating it. I then went back and started stepping through my attempted port of JonesForth to the MSP430.

The exercise 8-7 was Tic-Tac-Toe, so I decided to give that one a shot. It ended up being pretty easy based on the requirements they gave in the example. I didn't really like the the 1-9 placement, so I decided to do x, y top left as 0, 0 and bottom right as 2, 2 instead. It was pretty nice to see that the code I came up with wasn't vastly different than the answer they provided.


I also completed exercise 7-9 which was to display numbers 0-16 in bases 2, 10, & 16. This one was pretty trivial.

7-9.fth158 bytes
6 downloads
8-7.fth490 bytes
4 downloads

I ran a bit short on time for today. I ended up revisiting d* from yesterday. d* is a forth word to multiply a double length number by a single length number leaving a double length number on the stack ( d n -- d )

I had a fair amount of trouble with it, but coming back to the problem after sleeping on it helped. Using d* from my submission yesterday simplifies it quite a bit.

-- Edit after deadline --

After a bit more looking, it would seem I missed the m*/ word. d* becomes an alias for:

: d* 1 m*/ ;

Not only that, but mine runs at half the speed as that one on gforth. Oh well.

dstar.fth332 bytes
quad.fth154 bytes

Did some more of the exercises from chapter 7 of Starting Forth. This time was 7.3 & 7.7.

7.3 I found to be nearly trivial. The biggest annoyance there was doing a busy wait sleep function. After looking at their answer, it was somewhat nice to find out 500 MS could be used.

I thought 7.7 was going to be nearly trivial. I got a single cell version working very quickly. However, upon rereading the excercise, I realized they were specifically asking for a double cell variant (which makes sense in retrospect since I think they did a similar quadratic function in an earlier chapter.) It ended up taking me a fair amount of time to really work that out, but at least I had a working single cell variant to double check my work with.

I ended the night with playing around with defining a word: d* ( d n -- d ) However, I didn't really get it working.

quad.fth247 bytes
2 downloads
rings.fth141 bytes

Trying to work on some Forth, I've basically read through Starting Forth, but didn't do the examples. For tonight, I decided to pick an example and give it a go.

I ended up with:

VARIABLE i
: N-MAX
    0 i !
     BEGIN
        50 i +!
        i @ 0 <=
    UNTIL

    BEGIN
        i @ 1 - i !
        i @ 0>=
    UNTIL

    i @ .
;

I wasn't too disappointed with this solution. As I was attempting it, I realized +1 wasn't as fast as I had hoped. To compensate I over count, and then count back. The solution they give is here: http://www.forth.com/starting-forth/sf7/7-1.forth

Pay attention to the comments about the note. With that in mind I realized I overly complicated it with a variable (force of habit with every other programming languages.) I was able to very quickly iterate their solution to include the second loop after over counting in the first. Overall, not a bad night.

I got stuck mostly in setting up environments last night. I thought I hadn't really done much with my FIGnition yet, so I'd plug that in and go. It didn't take long to realize why I had been slow to really get full swing with it, QWERTY default. FIGkeys seems to allow most keys to be remapped, but requires a number pad. I decided to wait until tomorrow to get a PS2 or USB keyboard with a number pad.

Then I decided to get my BeagleBone Black setup. I tried Cloud9 a bit, but then decided to install Go. I forgot there weren't pre-built Go ARM/Linux packages. I pulled down Go from git, checked out the 1.4.1 tag, and started the build. When I realized it was going to take some time, I went on to the next thing.

Next thing was playing with the Launchpad some more trying to load mspforth, CamelForth, & Mecrisp, all without much luck of getting a serial connection. One thing that stuck out to me was that newer Launchpad boards potentially changed the USB serial pins. As a last ditch, even though none of the Forths seemed to support it, I fired up my new MSP430F5969 Launchpad, but Energia said I needed to update the drivers, which failed on Linux. Had to download a tool from TI that appeared to repair it. Tried again, and the update failed again. After repairing a second time, I decided to call it a night.

Overall, I didn't touch much code, but I did a bit with MSP430 assembly with naken_asm => msp430-gcc conversion, but nothing really worth posting.