The birth of seedForth
authorUlrich Hoffmann <uho@xlerb.de>
Wed, 13 Jun 2018 16:43:28 +0000 (18:43 +0200)
committerUlrich Hoffmann <uho@xlerb.de>
Wed, 13 Jun 2018 16:43:28 +0000 (18:43 +0200)
preForth/Makefile
preForth/seedForth-i386.pre [new file with mode: 0644]
preForth/seedForth-tokenizer.fs [new file with mode: 0644]
preForth/seedForthDemo.seedsource [new file with mode: 0644]

index 145af09..ea9bc17 100644 (file)
@@ -25,6 +25,11 @@ all: preForth simpleForth forth runforth
 runforth: ./forth hi.forth
        cat hi.forth - | ./forth
 
+.PHONY=runseedforth
+runseedforth: seedForth seedForthDemo.seed
+       cat seedForthDemo.seed | ./seedForth
+
+
 .PHONY=test
 test: preForth.pre preForth-$(PLATFORM)-backend.pre load-$(PLATFORM)-preForth.fs 
        cat preForth-$(PLATFORM)-backend.pre simpleForth.pre | $(HOSTFORTH) load-$(PLATFORM)-preForth.fs 
@@ -45,6 +50,9 @@ else
   EXT=asm
 endif
 
+seedForth-i386.asm: seedForth-i386.pre preForth
+       cat seedForth-i386.pre | ./preForth >seedForth-i386.asm
+
 # preForth connected to stdin - output to preForth.asm
 preForth.asm: preForth.pre preForth-$(PLATFORM)-backend.pre load-$(PLATFORM)-preForth.fs
        cat preForth-$(PLATFORM)-rts.pre preForth-rts.pre preForth-$(PLATFORM)-backend.pre preForth.pre \
@@ -130,7 +138,19 @@ simpleForth: simpleForth.$(UNIXFLAVOUR)
 %.asm: %.simple simpleForth simpleForth-$(PLATFORM)-rts.simple simpleForth-rts.simple
        cat simpleForth-$(PLATFORM)-rts.simple simpleForth-rts.simple $< | ./simpleForth >$@
 
+# ------------------------------------------------------------------------
+# seedForth
+# ------------------------------------------------------------------------
+seedForth.$(EXT): seedForth-$(PLATFORM).pre preForth
+       cat seedForth-$(PLATFORM).pre | ./preForth >seedForth.$(EXT)
+
+seedForth: seedForth.$(UNIXFLAVOUR)
+       cp seedForth.$(UNIXFLAVOUR) seedForth
+
+%.seed: %.seedsource seedForth-tokenizer.fs
+       gforth seedForth-tokenizer.fs $<
+
 
 .PHONY=clean
 clean:
-       rm -f *.asm *.o *.fas *.s *.c *.Darwin *.Linux preForthdemo simpleForthDemo simpleForth preForth forth
+       rm -f *.asm *.o *.fas *.s *.c *.Darwin *.Linux preForthdemo simpleForthDemo simpleForth preForth forth seedForth seedForthDemo.seed
diff --git a/preForth/seedForth-i386.pre b/preForth/seedForth-i386.pre
new file mode 100644 (file)
index 0000000..e2f2fad
--- /dev/null
@@ -0,0 +1,384 @@
+\ seedForth - seed it, feed it, grow it - i386 (32 bit) ITC flavour   uho 2018-04-13
+\ ----------------------------------------------------------------------------------
+\
+\  - registers:
+\      EAX, EDX  general purpose
+\      ESI  instruction pointer
+\      EBP  return stack pointer
+\      ESP  data stack pointer
+
+prelude
+;;; This is seedForth - a small, potentially interactive Forth, that dynamically
+;;; bootstraps from a minimal kernel.
+;;;
+;;;    cat seedForth.seed - | ./seedForth
+;;;
+;;; .seed-files are in byte-tokenized source code format.
+;;;
+;;; Use the seedForth tokenizer to convert human readable source code to byte-token form.
+;
+
+prefix
+format ELF 
+
+section '.bss' executable writable
+
+       DD 10000 dup(0)
+stck:  DD 16 dup(0)
+  
+       DD 10000 dup(0)
+rstck: DD 16 dup(0)
+
+_dp:    DD _start  ; dictionary pointer: points to next free location in memory
+       ; free memory starts at _start
+
+_hp:    DD 0       ; head pointer: points to first unused head
+_head:  DD 10000 dup (0)
+
+
+section '.text' executable writable align 4096
+
+public main 
+extrn putchar
+extrn getchar
+extrn fflush
+extrn exit
+extrn mprotect
+  
+macro next  {
+       lodsd
+       jmp dword [eax]
+}
+
+origin:
+
+main:  cld
+       mov esp, dword stck
+       mov ebp, dword rstck
+
+       ; make section writable
+       push ebp
+       mov ebp, esp
+       sub esp, 16
+       and esp, 0xfffffff0
+       mov dword [esp+8], 7  ; rwx
+       mov eax, memtop
+       sub eax, origin
+       mov dword [esp+4], eax
+       mov dword [esp], origin
+       call mprotect
+       mov esp, ebp
+       pop ebp
+       or eax, eax     ; error?   
+       jz main0
+       push ebp  
+       mov ebp, esp
+       push eax
+       and esp, 0xfffffff0
+       ; call __error    ; get error code on Mac OS
+       ; mov eax, [eax]
+       ; call __errno_location ; get error on Linux
+       ; mov eax, [eax]
+       mov [esp], eax
+       call exit
+
+main0: mov esi, main1
+       next
+
+main1: DD _cold
+       DD _bye  
+  
+_nest:  lea ebp, [ebp-4]
+        mov [ebp], esi
+        lea esi, [eax+4]
+        next
+
+_dodoes: ; ( -- addr ) \ call me
+        lea ebp, [ebp-4]  ; push IP
+        mov [ebp], esi
+        pop esi           ; set IP to caller
+_dovar: ; ( -- addr )
+        lea eax,[eax+4] ; to parameter field
+       push eax
+        next
+
+_O = 0
+  
+;
+
+
+code bye ( -- )
+    push ebp  
+    mov ebp, esp  
+    and esp, 0xfffffff0
+    mov eax, 0
+    mov [esp], eax
+    call exit
+;
+    
+code emit ( c -- )
+    pop eax
+
+    push ebp  
+    mov  ebp, esp
+    push eax 
+    and  esp, 0xfffffff0
+
+    mov dword [esp], eax
+    call putchar
+
+    mov eax, 0
+    mov [esp], eax
+    call fflush   ; flush all output streams
+
+    mov esp, ebp  
+    pop ebp  
+    next
+;
+
+code key ( -- c )
+        push ebp  
+        mov  ebp, esp
+        and  esp, 0xfffffff0
+        
+        call getchar
+        mov esp, ebp
+        pop ebp
+        cmp eax,-1
+        jnz key1
+        mov eax,4
+key1:   push eax
+        next
+;
+
+code dup ( x -- x x )
+        pop eax
+        push eax
+        push eax
+        next
+;
+
+code swap ( x y -- y x )
+        pop edx
+        pop eax
+        push edx
+        push eax
+        next
+;
+
+code drop ( x -- )
+        pop eax
+        next
+;
+
+code 0< ( x -- flag )
+        pop eax
+        or eax, eax
+        mov eax, 0
+        jns zless1
+        dec eax
+zless1: push eax
+        next
+;
+
+code ?exit ( f -- )
+        pop eax
+        or eax, eax
+        jz qexit1
+        mov esi, [ebp]
+        lea ebp,[ebp+4]
+qexit1: next
+;
+
+code >r ( x -- ) ( R -- x )
+        pop ebx
+        lea ebp,[ebp-4]
+        mov [ebp], ebx
+        next
+;
+
+code r> ( R x -- ) ( -- x )
+        mov eax,[ebp]
+        lea ebp, [ebp+4]
+        push eax
+        next
+;
+
+code - ( x1 x2 -- x3 )
+        pop edx
+        pop eax
+        sub eax, edx
+        push eax
+        next
+;
+
+code unnest ( -- )
+        mov esi,[ebp]
+        lea ebp,[ebp+4]
+        next
+;
+
+code lit ( -- )
+        lodsd
+        push eax
+        next
+;
+
+code @ ( addr -- x )
+        pop eax
+        mov eax,[eax]
+       push eax
+        next
+;
+
+code c@ ( c-addr -- c )
+        pop edx
+       xor eax, eax
+        mov al,byte [edx]
+       push eax
+        next
+;
+
+code ! ( x addr -- )
+        pop edx
+        pop eax
+        mov dword [edx],eax
+        next    
+;
+
+code c! ( c c-addr -- )
+        pop edx
+        pop eax
+        mov byte [edx], al
+        next
+;
+
+code execute ( xt -- ) \ native code: >r :
+        pop eax
+        jmp dword [eax]
+;
+
+code branch ( -- )  \ threaded code: r>  @ >r ;
+        lodsd
+        mov esi,eax
+        next
+;
+
+code ?branch ( f -- ) \ threaded code:  ?exit r> @ >r ;
+        pop eax
+        or eax,eax
+        jz _branchX
+       lea esi,[esi+4]
+        next
+;
+
+: negate ( n1 -- n2 )
+   0 swap - ;
+
+: + ( x1 x2 -- x3 )
+   negate - ;
+
+: 0= ( x -- flag )
+   0 swap ?exit drop -1 ;
+
+: ?dup ( x -- x x | 0 )
+   dup 0= ?exit dup ;
+
+: cells ( x1 -- x2 )
+   dup + dup + ;
+
+: +! ( x addr -- )
+   swap >r  dup @ r> +  swap ! ;
+
+: h@ ( i -- addr )
+   cells lit head + @ ;
+
+: h! ( x i -- )
+   cells lit head + ! ;
+
+: h, ( x -- )
+   lit hp @  h!   1 lit hp +! ;
+
+: here ( -- addr )
+   lit dp @ ;
+
+: allot ( n -- )
+   lit dp +! ;
+
+: , ( x -- )
+   here   1 cells allot  ! ;
+
+: c, ( c -- )
+   here   1 allot c! ;
+
+: interpreter ( -- )
+   key h@ execute   tail interpreter ;
+
+: compiler ( -- )
+   key ?dup 0= ?exit h@ ,   tail compiler ;
+
+: fun ( -- )
+   here h,  lit nest ,  compiler ;
+
+
+: create ( -- )
+   here h, lit dovar , ;
+
+: ,call ( x -- )
+   232 c, here >r  0 ,   here -   r> ! ;  \ call near 32bit
+
+: does ( -- )
+   r>   lit hp @ 1 - h@  ! ; \ set code field of last defined word
+
+: does> ( -- )
+   lit does ,
+   lit dodoes ,call ;
+
+
+: cold ( -- )
+   's' emit 'e' dup emit emit  'd' emit 10 emit
+   lit bye         h, \ 0   00
+   lit emit        h, \ 1   01
+   lit key         h, \ 2   02
+   lit dup         h, \ 3   03
+   lit swap        h, \ 4   04
+   lit drop        h, \ 5   05
+   lit 0<          h, \ 6   06
+   lit ?exit       h, \ 7   07
+   lit >r          h, \ 8   08
+   lit r>          h, \ 9   09
+   lit -           h, \ 10  0A
+   lit unnest      h, \ 11  0B
+   lit lit         h, \ 12  0C
+   lit @           h, \ 13  0D
+   lit c@          h, \ 14  0E
+   lit !           h, \ 15  0F
+   lit c!          h, \ 16  10
+   lit execute     h, \ 17  11
+   lit branch      h, \ 18  12
+   lit ?branch     h, \ 19  13
+   lit negate      h, \ 20  14
+   lit +           h, \ 21  15
+   lit 0=          h, \ 22  16
+   lit ?dup        h, \ 23  17
+   lit cells       h, \ 24  18
+   lit +!          h, \ 25  19
+   lit h@          h, \ 26  1A
+   lit h,          h, \ 27  1B
+   lit here        h, \ 28  1C
+   lit allot       h, \ 29  1D
+   lit ,           h, \ 30  1E
+   lit c,          h, \ 31  1F
+   lit fun         h, \ 32  20
+   lit interpreter h, \ 33  21
+   lit compiler    h, \ 34  22
+   lit create      h, \ 35  23
+   lit does>       h, \ 36  24
+   lit cold        h, \ 37  25
+   tail interpreter ;
+
+pre
+ _start: DB 43
+        DD 10000 dup (0)
+ memtop: DD 0
+;
diff --git a/preForth/seedForth-tokenizer.fs b/preForth/seedForth-tokenizer.fs
new file mode 100644 (file)
index 0000000..4058ec0
--- /dev/null
@@ -0,0 +1,57 @@
+\ seedForth tokenizer (byte-tokenized source code)
+
+\ load on on top of gforth   uho  2018-04-13
+
+\ -----------------------------
+
+WARNINGS OFF
+
+VARIABLE OUT
+
+: PROGRAM ( <name> -- )
+   BL WORD COUNT R/W CREATE-FILE THROW OUT ! ;
+
+: SUBMIT ( c -- )
+   PAD C!  PAD 1 OUT @ WRITE-FILE THROW ;
+
+: END ( -- )
+   .S CR 0 SUBMIT OUT @ CLOSE-FILE THROW BYE ;
+
+
+Variable #FUNS  0 #FUNS !
+: FUN: ( <name> -- )
+    CREATE #FUNS @ ,  1 #FUNS +!
+  DOES> @ SUBMIT ;
+
+FUN: bye       FUN: emit        FUN: key       FUN: dup                        \ 00 01 02 03
+FUN: swap      FUN: drop        FUN: 0<        FUN: ?exit              \ 04 05 06 07
+FUN: >r        FUN: r>          FUN: -         FUN: unnest             \ 08 09 0A 0B
+FUN: lit       FUN: @           FUN: c@        FUN: !                  \ 0C 0D 0E 0F
+FUN: c!        FUN: execute     FUN: branch    FUN: ?branch            \ 10 11 12 13
+FUN: negate    FUN: +           FUN: 0=        FUN: ?dup               \ 14 15 16 17
+FUN: cells     FUN: +!          FUN: h@        FUN: h,                 \ 18 19 1A 1B
+FUN: here      FUN: allot       FUN: ,         FUN: c,                 \ 1C 1D 1E 1F
+FUN: fun       FUN: interpreter FUN: compiler  FUN: create             \ 20 21 22 23
+FUN: does>     FUN: cold                                                                               \ 24 25
+
+: [ ( -- )  0 SUBMIT ;
+: ] ( -- )  compiler ;
+
+: ': ( <name> -- ) FUN: fun ;
+: ;' ( -- ) unnest [ ;
+
+: # ( x -- )  key  SUBMIT ;    \ x is placed in the token file as a single byte, as defined by key/SUBMIT
+: #, ( x -- ) lit [ # , ] ;    \ x is placed in memory as a cell-sized quantity (32/64 bit), as defined by comma
+
+\ Control structure macros
+
+: AHEAD ( -- addr ) branch [ here  0 # , ] ;
+: IF ( -- addr )   ?branch [ here  0 # , ] ;
+: THEN ( addr -- ) [ here swap ! ] ;
+: ELSE ( addr1 -- addr2 )  branch  [ here 0 # ,  swap ] THEN ;
+
+: BEGIN ( -- addr )  [ here ] ;
+: AGAIN ( addr -- )   branch [ , ] ;
+: UNTIL ( addr -- )  ?branch [ , ] ;
+: WHILE ( addr1 -- addr2 addr1 )  IF [ swap ] ;
+: REPEAT ( addr -- ) AGAIN THEN ;
diff --git a/preForth/seedForthDemo.seedsource b/preForth/seedForthDemo.seedsource
new file mode 100644 (file)
index 0000000..e10a414
--- /dev/null
@@ -0,0 +1,249 @@
+\ seedForth demo program source
+\
+\ tokenize with
+\
+\ gforth seedForth-tokinzer.fs seedForthDemo.seedsource
+\
+\ then pipe into seedForth:
+\
+\ cat seedForthDemo.seed | ./seedForth
+\
+
+program seedForthDemo.seed
+
+'o' # 'k' # \ push stack marker. Used eventually below.
+
+': ?ok ( o k -- o k )  10 #, emit  >r dup emit r> dup  emit ;'
+
+?ok
+
+10 # emit  '*' # dup emit emit             \ interpret numbers and words
+
+': 3*  dup dup + + ;'           \ defintions
+': 1-  1 #, - ;'                \ compile number and words
+
+\ output utilities
+': cr    ( -- ) 10 #,  emit ;'
+': space ( -- ) 32 #,  emit ;'
+': .digit ( n -- )  '0' #, + emit ;'
+
+': star ( -- ) '*' #, emit ;'
+
+': stars ( n -- )
+    ?dup IF BEGIN star 1- ?dup 0= UNTIL THEN ;'  \ standard Forth control structures
+
+': dash ( -- ) '-' #, emit ;'
+
+': dashes ( n -- )  BEGIN ?dup WHILE dash 1- REPEAT ;'
+
+': --- ( -- ) cr 80 #, dashes ;'
+
+': space ( -- ) 32 #, emit ;'
+
+': spaces ( n -- )
+    BEGIN ?dup 0= ?exit space 1- AGAIN ;' \ another loop variation
+
+---
+
+': countdown ( n -- )
+    ?dup 0= ?exit  dup cr .digit  1- countdown ;'  \ recursion
+
+cr  '2' # emit  '*' # emit  '3' # emit  '=' # emit 2 #  3*  .digit      \ interpret new definitions
+
+9 # countdown
+
+---
+
+': another-count-down ( n -- )
+     BEGIN dup WHILE dup cr .digit 1- REPEAT drop ;' \ standard Forth control structures
+
+5 # another-count-down
+
+---
+
+': yes? ( f -- )
+    IF 'Y' #, ELSE 'N' #, THEN emit ;'  \ standard Forth conditionals
+
+cr 0 # yes?  -1 # yes?   1 # yes?
+
+?ok  \ display ok again (for error analysis)
+
+---
+
+\ utility words
+
+': 1+ ( x1 -- x2 )  1 #, + ;'
+
+': over ( x1 x2 -- x1 x2 x1 )  >r dup r> swap ;'
+
+': 2drop ( x1 x2 -- )  drop drop ;'
+
+': nip ( x1 x2 -- x2 ) swap drop ;'
+
+\ ': c, ( c -- )  here  1 #, allot  c! ;'
+
+': /string ( x1 x2 x3 -- x4 x5 )   swap over - >r + r> ;'
+
+': count ( addr -- c-addr u )  dup 1+ swap c@ ;'
+
+': type ( c-addr u -- )
+    BEGIN dup WHILE  over c@ emit  1 #, /string  REPEAT  2drop ;'
+
+here  5 # c,   'H' # c,  'e' # c,  'l' # dup c, c,   'o' # c,
+
+cr count type
+
+\ more utility words
+
+': < ( n1 n2 -- f )  - 0< ;'
+': > ( n1 n2 -- f )  swap < ;'
+': = ( x1 x2 -- f )  - 0= ;'
+
+\ hex number output
+
+': .hexdigit ( n -- )  dup 9 #, > IF lit [ 'A' # 10 # - , ] ELSE '0' #, THEN + emit ;'
+
+': 2* ( x1 -- x2 )  dup + ;'
+
+
+\ if we don't have u2/ but only 2* and 0< we need to implement u2/ with a loop. Candidate for primitive
+': u2/ ( x1 -- x2 )
+   0 #, 8 #, CELLS 1-  BEGIN ?dup WHILE  >r 2*  over 0< IF 1+ THEN  >r 2* r> r> 1- REPEAT nip ;'
+
+': odd? ( x1 -- f )  dup u2/ 2* = 0= ;'
+
+': 2/mod ( x1 -- x2 r )  \ swapped results
+   dup u2/ swap odd? negate ;'
+
+': 16/mod ( x -- x r )  \ swapped results
+   2/mod >r  2/mod >r  2/mod >r  2/mod  2* r> + 2* r> + 2* r> + ;'
+
+': #### ( x -- )
+   16/mod >r 16/mod >r 16/mod >r  16/mod >r  16/mod >r  16/mod >r 16/mod >r
+      .hexdigit  r> .hexdigit   r> .hexdigit  r> .hexdigit  r> .hexdigit
+   r> .hexdigit  r> .hexdigit   r> .hexdigit  space ;'
+
+': (.) ( x -- )
+   ?dup IF  16/mod >r (.) r> .hexdigit THEN ;'
+
+': u. ( x -- )
+   ?dup IF (.) ELSE '0' #, emit THEN space ;'
+
+': . ( n -- )  dup 0< IF '-' #, emit negate THEN u. ;'
+
+cr 100 # negate .  \ display negative number
+
+cr here u.         \ display larger number
+cr
+
+?ok
+
+---
+
+\ create and defining words
+
+fun: V create 4 # ,   \ new token for tokenizer and new variable like definition
+
+cr V @ u.  \  get value:  4
+
+?ok
+
+---
+
+\ We must split defining words into two parts.
+\  1) Build up the new word with function index in seedForth
+\  2) Let the tokenizer create its symbol table entry (then invoke 1)
+
+': _value ( x -- ) create , [ does> ] @ ;'  \ a seedForth defining word 1)  \ execute does> as it is a compiling word
+: Value ( <name> x -- )  fun: _value ; \ macro 2)
+
+
+': _variable ( x -- )  create 0 #, , [ does> ]    ;'  \ a seedForth defining word
+: Variable  ( <name> -- ) fun: _variable ; \ macro
+
+fun: V1  5 # _value
+cr V1 u.  \ use value: 5
+6 # Value v4   v4 u.  \ values are initialized from stack: 6
+
+
+fun: V2  _variable
+7 # V2 +!  V2 @ u.   8 # V2 !  V2 @ u.  \ fetch and store value: 7 8
+
+
+': doconst ( x -- ) [ does> ] @ ;'  \ a does>  w/o creat path sets behavour
+: Constant  ( <name> x -- ) fun: create , doconst ; \ macro
+
+fun: nine create
+  9 # ,   \ parameter field
+  doconst \ set behaviour of last word
+
+nine . \ display constant: 9
+
+0 # Constant zero  zero .  \ constants are similar to values here: 0
+
+?ok
+---
+
+
+\ structured data
+
+': _field ( addr -- addr' ) create over , + [ does> ] @ + ;'
+: Field ( <name> offset size -- offset' ) fun: _field ;
+
+\ define structure
+0 #
+
+1 # cells Field >name
+2 # cells Field >date
+
+Value #person
+
+fun: p1 create #person allot
+
+
+
+cr p1 u.       \ start of structure
+
+p1 >name u.    \ address calculation
+
+p1 >date u.    \ address calculation
+
+cr #person u.  \ size of structure
+
+?ok
+---
+
+\ Defered words
+
+': dodefer ( -- )  [ does> ] @ execute ;'
+: Defer  ( <name> -- ) fun: create ] star [  dodefer ;    \ macro, star is default behaviour
+
+': >body ( xt -- body )  1 #, cells + ;'
+
+': ' ( --  x )  key h@ ;'
+
+': is ( xt -- )  ' >body ! ;'
+
+
+Defer d1
+
+cr d1 d1 d1 \ display stars
+
+cr ' dash dup .  execute            \ get execution token of definition
+
+' dash is d1 \ set behaviour of deferred word
+
+cr d1 d1 d1 \ now display dashes
+
+?ok
+
+cr 80 # stars
+
+
+?ok
+cr
+
+2drop
+
+
+end