Dave's nasmjf Dev Log 17

Created: 2022-07-22

nasmjf home

This is an entry in my developer’s log series written between December 2021 and August 2022 (started project in September). I wrote these as I completed my port of the JONESFORTH assembly language Forth interpreter.

← Previous 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Next →
    Now we come to a bit of a challenge. The method of
    starting JonesFORTH with the rest of the language
    implementation *and* still allowing user input on
    STDIN is quite clever:

        cat jonesforth.f - | ./jonesforth

    That is, have cat read the FORTH source file and then
    whatever is supplied on STDIN (using the standard
    '-' parameter to indicate STDIN in place of a file)
    and pipe the lot to the jonesforth executable.

    Unfortunately, it crashes my port:

eeepc:~/nasmjf$ cat jonesforth/jonesforth.f - | ./nasmjf

Segmentation fault

    So we have a problem.

    And this is turning out to be hard to test.

    Well, it seems there are a couple routes I could take
    here:

    1. Hand-type the contents of jonesforth.f and see what
       causes the crash. I'm okay with the effort - I plan
       to go through that source line-by-line to understand
       it anyway. But the problem is: how do I incrementally
       save what I've typed so far?

    2. Use divide-and-conquer to find out how much of
       'jonesforth.f' I can run before we get a crash. That
       would probably work - but it's going to be an annoying
       process and I'll be working blind.

    3. Figure out how to read in 'jonesforth.f' on startup
       as part of the executable. This will allow debugging
       in GDB to continue (no doubt I could figure out some
       arcane way of redirecting or piping that to STDIN
       while in a GDB session or attach to the process or
       something...but I'm not enthusiastic about figuring
       that out. I'd rather write code.)

    As you can likely guess, I'm going with Option 3. I'm going
    to have the nasmjf executabe read 'jonesforth.f' upon
    startup. The plan is something like this:

    A. Open the file using syscalls
    B. Read into a fixed-size reserved buffer
    C. Keep track of line numbers for reference?
    D. Feed the source into the interpreter somehow
    E. Now I can debug the crash?
    F. When EOF reached, close the file and start accepting
       interpreter input from STDIN again.

    The only challenging part, it seems to me, is D: feeding the
    file into the interpreter. Words like KEY are currently
    hard-coded to read from STDIN, right? So maybe I need a global
    switch to read from my line buffer instead?

    Let's get started!

    For A, I've got this near the top:

        SECTION .bss
        line_buffer:  resb 1024 ; for storing source lines from file

    Then

        SECTION .data
        jfsource:   db "jonesforth/jonesforth.f", 0h

    And then reading the file - another snippet adapted from
    asmtutor.com!


Breakpoint 1, _start () at nasmjf.asm:82
82          cld    ; Clear the "direction flag" which means the string instructions (such
      ...
118         mov ecx, 0                ; read only flag for open
119         mov ebx, jfsource
120         mov eax, __NR_open
121         int 80h                   ; fd now in eax
124         mov edx, 1024              ; amt to read
125         mov ecx, line_buffer       ; address of .bss reserved space
126         mov ebx, eax               ; fd from pushed eax above
127         mov eax, __NR_read
128         int 80h
(gdb) x/s (int)&line_buffer
0x804db30 <line_buffer>:        "\\ -*- text -*-\n\\\tA sometimes minimal FORTH
compiler and tutorial for Linux / i386 systems. -*- asm -*-\n\\\tBy Richard W.M.
Jones <rich@annexia.org> http://annexia.org/forth\n\\\tThis is PUBLIC DOMAIN"...

    Awesome! Of course it works. It's a trivial operation, even
    in assembly.

    Anyway, now to figure out how to feed the source into the
    interpreter.

    Weeks have passed. Progress has gone at a snail's pace and
    I've often fallen asleep with barely a single assembly
    instruction written.

    Nevertheless, I think I'm getting close.

    TIME PASSES...

    Okay, a month later and I've got it reading a test.f file
    to make sure I can read in N lines of a Forth source file
    when the interpreter starts and then hand input back to
    the keyboard.

    Here's the entirety of test.f:

        1 .
        2 .
        3 .
        4 .
        5 .
        6 .
        7 .
        8 .

    So it's just Forth for printing the line number on each line
    so I can see if it correctly stops after six lines.

        jfsource: db "test.f" , 0h
        %assign __lines_of_jf_to_read 6

    What does it look like when it runs? Let's find out:

123456

    Yay! It reads and executes six lines, then waits for user input.
    Perfect!

    This took about a month because of a ton of stupid bugs, off-by-
    one errors, and that sort of thing. But mostly because I was
    really tired and kept falling asleep.

    Anyway, now I can read N lines of jonesforth.f and test it out
    a bit at a time!

    Next night: okay, here goes nothing:

        jfsource:   db "jonesforth/jonesforth.f", 0h
        %assign __lines_of_jf_to_read 52

    This reads to line 52 of jonesforth.f, which is a bunch of comments
    and a definiton of / and MOD:

        : / /MOD SWAP DROP ;
        : MOD /MOD DROP ;

10 5 / .
2
10 5 MOD .
0
10 3 / .
3
10 3 MOD .
1

    Wow, that actually works! So now I can start logging my
    progress as I load and test more of the FORTH portion of
    the JonesFORTH implementation.

    Now I'm loading to line 70, which contains definitions for
    some more handy primitives.

    I'll test them out now:

3 5 . SPACE .
5 3

3 5 . CR .
5
3

3 NEGATE .
4294967293

TRUE .
1
FALSE .
0
TRUE NOT .
0
FALSE NOT .
1

    Heh, I'll just trust at the negaative value is correct for
    now.

    So, this is great. I'll just keep working my way down
    jonesforth.f until I've tested everything and/or reproduced
    the crash I was experiencing before.
← Previous 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Next →