diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..14dfdda --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tests"] + path = tests + url = https://github.com/gerryjackson/forth2012-test-suite diff --git a/Makefile b/Makefile index d6c6eff..e54b3fb 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ cppcheck: libalee alee*.cpp *dict.hpp test: standalone - echo "\nbye\n" | ./alee-standalone forth/core-ext.fth forth/test/tester.fr forth/test/core.fr + echo "bye" | ./alee-standalone forth/core-ext.fth tests/src/tester.fr tests/src/core.fr $(LIBFILE): $(OBJFILES) $(AR) crs $@ $(OBJFILES) diff --git a/README.md b/README.md index eca6085..247624f 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,26 @@ # Alee Forth -Alee Forth is a concise Forth implementation written in modern C++ that aims for portability, minimal program size, and execution efficiency. +Alee Forth is a concise Forth implementation written in modern C++ that aims for portability, minimal memory footprint, and execution efficiency. ## Cross-platform compatibility -Alee Forth relies on the C++20 standard. It *does not* rely on any operating system. As a result, portability extends down to microcontroller targets with < 1 kB of memory. See the `msp430` folder for an example of such a port. +Alee Forth relies on the C++20 standard. It *does not* rely on any operating system. As a result, portability extends down to microcontroller targets with < 16kB flash and < 1 kB of RAM. See the `msp430` folder for an example of such a port. -System-specific functionality is obtained through a `sys` Forth word. This word calls a user-supplied C++ function that implements whatever functionality is needed. +System-specific functionality is achieved through a `sys` Forth word. This word calls a user-supplied C++ function that implements whatever functionality is needed. ## Forth compatibility -Alee implements the entire "core" and majority of the "core extension" [word-sets](https://forth-standard.org/standard/core). Implementation is tracked in `compat.txt` with missing words listed below. Fundamental words are hard-coded into Alee while the rest of the implementation is found in `forth/core.fth` and `forth/core-ext.fth`. Running Alee without these implementation files will leave you with a very minimal word-set. These files may be compiled into the Alee binary by building the `standalone` target. +Alee Forth uses the [Forth 2012 test suite](https://github.com/gerryjackson/forth2012-test-suite) to ensure standards compliance. The entire "core" [word-set](https://forth-standard.org/standard/core) is implemented as well as most of the "core extension" word-set. The compiled program contains a minimal set of fundamental words with libraries in the `forth` directory supplying these larger word-sets. The "core" word-set can be compiled into the program by building the `standalone` target. + +**Missing** core extension words: -**Missing** core extensions: ``` .R HOLDS PAD PARSE PARSE-NAME REFILL RESTORE-INPUT S\" SAVE-INPUT SOURCE-ID U.R U> UNUSED WITHIN [COMPILE] ``` -Alee aims for compliance with common Forth standards like Forth 2012 and ANS Forth. Compliance is tested using a [Forth 2012 test suite](https://github.com/gerryjackson/forth2012-test-suite). Supported test files are in the `test` directory with tests for unimplemented words commented out. - ## Building -Alee requires `make` and a compiler that supports C++20. Simply running `make` will produce the `libalee.a` library and a REPL binary named `alee`. You will likely want to pass in the core implementation files by calling `./alee forth/core.fth forth/core-ext.fth`. +Alee requires `make` and a compiler that supports C++20. Simply running `make` will produce the `libalee.a` library and a REPL binary named `alee`. The core word-sets can be passed into `alee` via the command line: `./alee forth/core.fth forth/core-ext.fth`. Other available build targets: @@ -30,5 +29,5 @@ Other available build targets: * `standalone`: Builds the core dictionary (`core.fth`) into the binary. * `msp430-prep` and `msp430`: Builds a binary for the [MSP430G2553](https://www.ti.com/product/MSP430G2553) microcontroller. See the `msp430` folder for more information. -If building for a new platform, review these files: `Makefile`, `libalee/types.hpp`, and `libalee/state.hpp`. +If building for a new platform, review these files: `Makefile`, `libalee/types.hpp`, and `libalee/state.hpp`. It is possible to modify the implementation to use 32-bit words, but this will require re-writing the core word-sets. diff --git a/forth/core.fth b/forth/core.fth index 071af33..360d269 100644 --- a/forth/core.fth +++ b/forth/core.fth @@ -156,13 +156,14 @@ swap postpone literal postpone literal ; imm : ." postpone s" state @ if ['] type , else type then ; imm -: create align here - 1 cells 1 chars - allot - bl word count swap drop - 1 chars allot - swap over over ! swap allot align +: create align here dup _latest @ - 1 1 cells 8 * 6 - << 1- swap <= + dup if -1 6 << , then 0 , >r + begin key? if key else bl then dup bl <> while + c, 1 over +! repeat drop align ['] _lit , here 3 cells + , ['] exit dup , , - dup @ 31 & over _latest @ - 6 << or over ! _latest ! ; + dup _latest @ - r> if + over cell+ else 6 << over then +! _latest ! ; + : _does> _latest @ dup @ 31 & + cell+ aligned 2 cells + ['] _jmp over ! cell+ r@ 1 cells - @ swap ! ; diff --git a/libalee/corewords.cpp b/libalee/corewords.cpp index 62b0322..1a39b7e 100644 --- a/libalee/corewords.cpp +++ b/libalee/corewords.cpp @@ -178,8 +178,14 @@ execute: cell = state.pop(); dcell = (cell - state.dict.latest()) << 6; - state.dict.write(cell, - (state.dict.read(cell) & 0x1F) | static_cast(dcell)); + if (dcell > (((1 << (sizeof(Cell) * 8 - 6)) - 1) << 6)) { + state.dict.write(cell, + (state.dict.read(cell) & 0x1F) | static_cast(((1 << (sizeof(Cell) * 8 - 6)) - 1) << 6)); + state.dict.write(static_cast(cell) + sizeof(Cell), static_cast(dcell >> 6)); + } else { + state.dict.write(cell, + (state.dict.read(cell) & 0x1F) | static_cast(dcell)); + } state.dict.latest(cell); break; case 27: // _jmp0 diff --git a/libalee/dictionary.cpp b/libalee/dictionary.cpp index 0d225e0..6c359bd 100644 --- a/libalee/dictionary.cpp +++ b/libalee/dictionary.cpp @@ -64,6 +64,9 @@ void Dictionary::addDefinition(Word word) noexcept Cell wsize = word.size(); add(wsize); + if (alignhere() - latest() >= ((1 << (sizeof(Cell) * 8 - 6)) - 1)) + add(0); + auto addr = allot(wsize); auto it = word.begin(this); const auto end = word.end(this); @@ -81,14 +84,25 @@ Addr Dictionary::find(Word word) noexcept for (;;) { const Addr l = read(lt); const Addr len = l & 0x1F; + Word lw; - const auto lw = Word::fromLength(lt + sizeof(Cell), len); - if (equal(word, lw)) - return lt; - else if (lt == Begin) - break; - else - lt -= l >> 6; + if ((l >> 6) < 1023) { + lw = Word::fromLength(lt + sizeof(Cell), len); + if (equal(word, lw)) + return lt; + else if (lt == Begin) + break; + else + lt -= l >> 6; + } else { + lw = Word::fromLength(lt + 2 * sizeof(Cell), len); + if (equal(word, lw)) + return lt; + else if (lt == Begin) + break; + else + lt -= static_cast(read(lt + sizeof(Cell))); + } } return 0; @@ -96,8 +110,13 @@ Addr Dictionary::find(Word word) noexcept Addr Dictionary::getexec(Addr addr) noexcept { - const Addr len = read(addr) & 0x1Fu; + const Addr l = read(addr); + const Addr len = l & 0x1Fu; + addr += sizeof(Cell); + if ((l >> 6) == 1023) + addr += sizeof(Cell); + addr += len; return aligned(addr); } diff --git a/tests b/tests new file mode 160000 index 0000000..45b7ecc --- /dev/null +++ b/tests @@ -0,0 +1 @@ +Subproject commit 45b7ecc1e37d8fdd1bd9a29ec52a15bf7ac69d7f