Compare commits

..

49 Commits
llvm ... master

Author SHA1 Message Date
Clyne 6264c07c3b
add .gitattributes 7 months ago
Clyne 9ac70bfc0e
add dict impl to libalee section 10 months ago
Clyne ebbde43fa9 Merge branch 'documentation' 10 months ago
Clyne 23d746d646
msp430: add spi, update uart examples 10 months ago
Clyne af51fb5bdf
add LIBALEE_SECTION; minor fixes 10 months ago
Clyne 5162349f92
CHAR return zero on no input 10 months ago
Clyne f21da699e5
msp430: add rtc, analog, button support 10 months ago
Clyne cdf19490df
no more cstring; 16mhz/115200 msp430; fix dict init 10 months ago
Clyne 69152efdad
msp430: reduce excess memory usage; trim linker script 10 months ago
Clyne f8f73362a8
msp430: add uart example 10 months ago
Clyne fbaf08aa6c
msp430: build reg data with bash, not make 10 months ago
Clyne 957cf676ff
msp430: more dict space; prepare for hal impl 10 months ago
Clyne 494bd41b64
msp430: lpm support; . uses base; expand dict size 10 months ago
Clyne 0810456e9c
MaxDistance constant; some .cpp comments 11 months ago
Clyne 513136558e
coreword opcodes via token(); calculated WordCount 11 months ago
Clyne 4bf01807b2
precalculate some literals 11 months ago
Clyne 2b64fbdfb8
document some core words; reorganize 11 months ago
Clyne 6496152f57
add doxygen support 11 months ago
Clyne d6ee894c13
msp430: set SR on exit when in ISR 11 months ago
Clyne 8e7cb05cfb Merge branch 'master' of ssh://code.bitgloo.com:222/bitgloo/alee-forth 11 months ago
Clyne a7b3cc1604
msp430: clean up some build flags 11 months ago
Clyne 7e10d0855c
msp430: add compressed register/flag wordset 11 months ago
Clyne 789791692a
make parser extendable 11 months ago
Clyne 8f30a73807 Delete 'forth/hal.fth' 11 months ago
Clyne e175ab21b3
interrupt support; better reg words 11 months ago
Clyne 974f49ca16
fix standalone 11 months ago
Clyne 9bb53dc9d7
forgot to push msp430 dict impl 11 months ago
Clyne 4a1ec24592
msp430: include core-ext and tools 11 months ago
Clyne ea026f8d67
running on the devboard 11 months ago
Clyne b26edffda0
msp430 impl targets msp430fr2476 11 months ago
Clyne 6dcf780a3b
fix if condition on 16-bit cpus 11 months ago
Clyne 6bd7338414
add SOURCE-ID 11 months ago
Clyne 97a590fc8e
add PARSE; fixes to actually comply with all tests 11 months ago
Clyne 91566e20e8
fix core and core-ext impl to pass all tests 11 months ago
Clyne 3bb6ecbb23
fix MARKER 11 months ago
Clyne ddba135e7d
add [COMPILE], fix :NONAME 11 months ago
Clyne 79a15b78b1
remove compat.txt 11 months ago
Clyne 5ee8f7e01d
add .R PAD U.R U> UNUSED WITHIN 11 months ago
Clyne 6b1955a69e
remove old tests 11 months ago
Clyne 650a344aad
support long definitions; add tests as submodule 11 months ago
Clyne 7381e87be6
100% core test passing 11 months ago
Clyne 194acf022d
implement HOLDS 11 months ago
Clyne 6bd7c01389
implement pictured numeric output 11 months ago
Clyne 3d7a45e5cd
msp430.fth 11 months ago
Clyne 5991370657
consteval and other refactoring 11 months ago
Clyne 70e399b498
switch to c++20 11 months ago
Clyne 0b2ef84376
align builtin dictionary 11 months ago
Clyne f7a9103bbe
more refactoring, object organization 11 months ago
Clyne 15c0c2f789
some class refactoring 11 months ago

1
.gitattributes vendored

@ -0,0 +1 @@
msp430/msp430fr2476.h linguist-vendored

5
.gitignore vendored

@ -1,8 +1,13 @@
.*
*.o
*.dat
*.bin
*.lzss
alee
alee-msp430
alee-standalone
libalee.a
core.fth.h
doc/
msp430/lzss
msp430/msp430fr2476_all.h

3
.gitmodules vendored

@ -0,0 +1,3 @@
[submodule "tests"]
path = tests
url = https://github.com/gerryjackson/forth2012-test-suite

2303
Doxyfile

File diff suppressed because it is too large Load Diff

@ -1,49 +1,62 @@
CXXFLAGS += -std=c++17 -g3 -ggdb -O0 \
CXXFLAGS += -std=c++20 -g3 -ggdb -O0 \
-pedantic -Wall -Wextra -Werror -Weffc++ -Wconversion \
-fno-exceptions -fno-threadsafe-statics -fno-rtti #-fstack-usage
-fno-exceptions -fno-rtti
CXXFILES := $(wildcard libalee/*.cpp)
OBJFILES := $(subst .cpp,.o,$(CXXFILES))
LIBFILE := libalee/libalee.a
all: alee
STANDALONE := forth/core.fth
msp430: CXX := msp430-elf32-g++
msp430: AR := msp430-elf32-gcc-ar
msp430: CXXFLAGS += -I.
msp430: CXXFLAGS += -Os -mmcu=msp430g2553 -ffunction-sections -fdata-sections
msp430: CXXFLAGS += -DMEMDICTSIZE=200 -flto
msp430: LDFLAGS += -L/opt/msp430-elf32/include -Tmsp430/msp430g2553.ld -Wl,-gc-sections
msp430: clean-lib msp430/alee-msp430
all: alee
small: CXXFLAGS += -Os
msp430: CXX := msp430-elf-g++
msp430: AR := msp430-elf-gcc-ar
msp430: CXXFLAGS += -I. -I/usr/msp430-elf/usr/include
msp430: CXXFLAGS += -Os -mmcu=msp430fr2476 -ffunction-sections -fdata-sections
msp430: CXXFLAGS += -flto -fno-asynchronous-unwind-tables -fno-threadsafe-statics -fno-stack-protector
msp430: CXXFLAGS += -DALEE_MSP430_HOST
msp430: LDFLAGS += -L msp430 -T msp430fr2476.ld -Wl,-gc-sections -Wl,--no-warn-rwx-segments
msp430: msp430/alee-msp430
msp430-prep: CXXFLAGS += -DALEE_MSP430 -Imsp430
msp430-prep: msp430/msp430fr2476_all.h
msp430-prep: STANDALONE += forth/core-ext.fth forth/tools.fth forth/msp430.fth
msp430-prep: core.fth.h
msp430-prep: clean-lib
small: CXXFLAGS += -Os -fno-asynchronous-unwind-tables -fno-threadsafe-statics -fno-stack-protector
small: alee
fast: CXXFLAGS += -O3 -march=native -mtune=native -flto
fast: alee
standalone: core.fth.h
standalone: alee-standalone
alee: $(LIBFILE)
msp430/alee-msp430: $(LIBFILE)
alee-standalone: core.fth.h $(LIBFILE)
alee-standalone: $(LIBFILE)
cppcheck:
cppcheck --enable=warning,style,information --disable=missingInclude \
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)
core.fth.h: alee.dat
xxd -i $< > $@
sed -i "s/unsigned /static const &/" $@
sed -i "s/\[\]/\[ALEE_RODICTSIZE\]/" $@
alee.dat: alee $(STANDALONE)
echo "3 sys" | ./alee $(STANDALONE)
alee.dat: alee forth/core.fth
echo "3 sys" | ./alee forth/core.fth
msp430/msp430fr2476_all.h:
$(MAKE) -C msp430
clean: clean-lib
rm -f alee alee-standalone msp430/alee-msp430

@ -1,41 +1,33 @@
# Alee Forth
Alee 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 relies on the C++17 standard. Alee *does not* rely on operating-system-specific functions, making portability easy.
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.
The goal of portability extends down to microcontroller targets with kilobytes of memory. See the `msp430` target for an example of a port.
System-specific functionality is obtained through a `sys` Forth word. This word calls a user-supplied C++ function that implements the necessary (or any additional) functionality.
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 a large majority of the "core" and "core extension" [word sets](https://forth-standard.org/standard/core). Implementation is tracked in `compat.txt`, with missing words listed below. Fundamental words are built into Alee (written in C++); the rest of the implementation is in `core.fth` and `core-ext.fth`.
Running Alee without `core.fth` or `core-ext.fth` passed as arguments will leave you with a minimal word set. The `standalone` target will package the `core.fth` dictionary into the program.
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 features:
* Pictured numeric output conversion (e.g. `<# #>`)
**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]
PARSE-NAME REFILL RESTORE-INPUT S\" SAVE-INPUT
```
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 C++17-compatible compiler. Simply running `make` will produce the `libalee.a` library and a REPL binary named `alee`. Note that this binary has no built-in libraries; these can be passed in by calling `./alee core.fth 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`.
There are other build targets:
Other available build targets:
* `small`: Optimize for minimal binary size.
* `fast`: Optimize for maximum performance on the host system.
* `standalone`: Builds the core dictionary (`core.fth`) into the binary.
* `msp430`: Builds a binary for the [MSP430G2553](https://www.ti.com/product/MSP430G2553) microcontroller. The `standalone` target must be built first for the core dictionary.
* `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, see `Makefile`, `types.hpp`, and `state.hpp` for available configuration options.
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.

@ -16,14 +16,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "alee.hpp"
#include "libalee/alee.hpp"
#include "splitmemdict.hpp"
#include <array>
#include <charconv>
#include <fstream>
#include <iostream>
#include <vector>
#define ALEE_RODICTSIZE
#include "core.fth.h"
static bool okay = false;
@ -34,7 +36,8 @@ static void parseFile(State&, std::istream&);
int main(int argc, char *argv[])
{
SplitMemDict<alee_dat_len> dict (alee_dat);
(void)alee_dat_len;
SplitMemDict<sizeof(alee_dat)> dict (alee_dat);
State state (dict, readchar);
std::vector args (argv + 1, argv + argc);
@ -89,13 +92,8 @@ void user_sys(State& state)
state.dict.read(Dictionary::Base));
std::cout << buf << ' ';
break;
case 1: // u.
{
Addr ucell = static_cast<Addr>(state.pop());
std::to_chars(buf, buf + sizeof(buf), ucell,
state.dict.read(Dictionary::Base));
std::cout << buf << ' ';
}
case 1: // unused
state.push(static_cast<Addr>(state.dict.capacity() - state.dict.here()));
break;
case 2: // emit
std::cout << static_cast<char>(state.pop());

@ -16,17 +16,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "alee.hpp"
#include "libalee/alee.hpp"
#include "memdict.hpp"
#include <algorithm>
#include <charconv>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <vector>
static void compile(State&);
#ifdef ALEE_MSP430
#include "lzss.h"
static const
#include "msp430fr2476_all.h"
static Error findword(State&, Word);
#endif // ALEE_MSP430
static bool okay = false;
@ -38,6 +41,9 @@ int main(int argc, char *argv[])
{
MemDict dict;
State state (dict, readchar);
#ifdef ALEE_MSP430
Parser::customParse = findword;
#endif // ALEE_MSP430
dict.initialize();
@ -94,13 +100,8 @@ void user_sys(State& state)
state.dict.read(Dictionary::Base));
std::cout << buf << ' ';
break;
case 1: // u.
{
Addr ucell = static_cast<Addr>(state.pop());
std::to_chars(buf, buf + sizeof(buf), ucell,
state.dict.read(Dictionary::Base));
std::cout << buf << ' ';
}
case 1: // unused
state.push(static_cast<Addr>(state.dict.capacity() - state.dict.here()));
break;
case 2: // emit
std::cout << static_cast<char>(state.pop());
@ -111,9 +112,6 @@ void user_sys(State& state)
case 4: // load
load(state);
break;
case 5: // compile
compile(state);
break;
default:
break;
}
@ -165,37 +163,48 @@ void parseFile(State& state, std::istream& file)
}
}
// Prints all compiled words, their start addresses, and their "disassembly".
// Hopefully, it won't be too difficult to translate these into LLVM IR.
void compile(State& state)
#ifdef ALEE_MSP430
#define LZSS_MAGIC_SEPARATOR (0xFB)
static char lzword[32];
static int lzwlen;
static char lzbuf[32];
static char *lzptr;
Error findword(State& state, Word word)
{
auto& dict = state.dict;
Addr latest = dict.latest();
Addr attr = 0;
do {
Addr oldlen = attr >> 6;
latest -= oldlen;
attr = dict.read(latest);
auto lw = Word::fromLength(latest + sizeof(Cell), attr & 0x1F);
if (!(attr & Dictionary::Immediate)) {
Addr start = dict.getexec(latest);
Addr len = oldlen;
len -= start;
len += latest;
std::for_each(lw.begin(&dict), lw.end(&dict), putchar);
std::cout << " @ " << start << std::endl;
for (Addr i = 0; i < len; i += sizeof(Cell)) {
Addr addr = start;
addr += i;
std::cout << '\t' << (Addr)dict.read(addr) << ' ';
char *ptr = lzword;
for (auto it = word.begin(&state.dict); it != word.end(&state.dict); ++it) {
*ptr = *it;
if (islower(*ptr))
*ptr -= 32;
++ptr;
}
lzwlen = (int)(ptr - lzword);
lzptr = lzbuf;
lzssinit(msp430fr2476_all_lzss, msp430fr2476_all_lzss_len);
auto ret = decode([](int c) {
if (c != LZSS_MAGIC_SEPARATOR) {
*lzptr++ = (char)c;
} else {
if (lzwlen == lzptr - lzbuf - 2 && std::equal(lzbuf, lzptr - 2, lzword)) {
lzwlen = (*(lzptr - 2) << 8) | *(lzptr - 1);
return 1;
} else {
lzptr = lzbuf;
}
std::cout << std::endl;
}
} while (latest != Dictionary::Begin);
return 0;
});
if (ret == EOF) {
return Error::noword;
} else {
Parser::processLiteral(state, (Cell)lzwlen);
return Error::none;
}
}
#endif // ALEE_MSP430

@ -1,3 +0,0 @@
#include "libalee/parser.hpp"
#include "libalee/state.hpp"

@ -1,187 +0,0 @@
6.1 Core words
yes 6.1.0010 !
6.1.0030 #
6.1.0040 #>
6.1.0050 #S
yes 6.1.0070 '
yes 6.1.0080 (
yes 6.1.0090 *
yes 6.1.0100 */
yes 6.1.0110 */MOD
yes 6.1.0120
yes 6.1.0130 +!
yes 6.1.0140 +LOOP
yes 6.1.0150 ,
yes 6.1.0160 -
yes 6.1.0180 .
yes 6.1.0190 ."
yes 6.1.0230 /
yes 6.1.0240 /MOD
yes 6.1.0250 0<
yes 6.1.0270 0=
yes 6.1.0290 1+
yes 6.1.0300 1-
yes 6.1.0310 2!
yes 6.1.0320 2*
yes 6.1.0330 2/
yes 6.1.0350 2@
yes 6.1.0370 2DROP
yes 6.1.0380 2DUP
yes 6.1.0400 2OVER
yes 6.1.0430 2SWAP
yes 6.1.0450 :
yes 6.1.0460 ;
yes 6.1.0480 <
6.1.0490 <#
yes 6.1.0530 =
yes 6.1.0540 >
yes 6.1.0550 >BODY
yes 6.1.0560 >IN
yes 6.1.0570 >NUMBER
yes 6.1.0580 >R
yes 6.1.0630 ?DUP
yes 6.1.0650 @
yes 6.1.0670 ABORT
yes 6.1.0680 ABORT"
yes 6.1.0690 ABS
yes 6.1.0695 ACCEPT
yes 6.1.0705 ALIGN
yes 6.1.0706 ALIGNED
yes 6.1.0710 ALLOT
yes 6.1.0720 AND
yes 6.1.0750 BASE
yes 6.1.0760 BEGIN
yes 6.1.0770 BL
yes 6.1.0850 C!
yes 6.1.0860 C,
yes 6.1.0870 C@
yes 6.1.0880 CELL+
yes 6.1.0890 CELLS
yes 6.1.0895 CHAR
yes 6.1.0897 CHAR+
yes 6.1.0898 CHARS
yes 6.1.0950 CONSTANT
yes 6.1.0980 COUNT
yes 6.1.0990 CR
yes 6.1.1000 CREATE
yes 6.1.1170 DECIMAL
yes 6.1.1200 DEPTH
yes 6.1.1240 DO
yes 6.1.1250 DOES>
yes 6.1.1260 DROP
yes 6.1.1290 DUP
yes 6.1.1310 ELSE
yes 6.1.1320 EMIT
yes 6.1.1345 ENVIRONMENT? (always false)
yes 6.1.1360 EVALUATE
yes 6.1.1370 EXECUTE
yes 6.1.1380 EXIT
yes 6.1.1540 FILL
yes 6.1.1550 FIND
yes 6.1.1561 FM/MOD
yes 6.1.1650 HERE
6.1.1670 HOLD
yes 6.1.1680 I
yes 6.1.1700 IF
yes 6.1.1710 IMMEDIATE (as "imm")
yes 6.1.1720 INVERT
yes 6.1.1730 J
yes 6.1.1750 KEY
yes 6.1.1760 LEAVE
yes 6.1.1780 LITERAL
yes 6.1.1800 LOOP
yes 6.1.1805 LSHIFT
yes 6.1.1810 M*
yes 6.1.1870 MAX
yes 6.1.1880 MIN
yes 6.1.1890 MOD
yes 6.1.1900 MOVE
yes 6.1.1910 NEGATE
yes 6.1.1980 OR
yes 6.1.1990 OVER
yes 6.1.2033 POSTPONE
yes 6.1.2050 QUIT
yes 6.1.2060 R>
yes 6.1.2070 R@
yes 6.1.2120 RECURSE
yes 6.1.2140 REPEAT
yes 6.1.2160 ROT
yes 6.1.2162 RSHIFT
yes 6.1.2165 S"
yes 6.1.2170 S>D
6.1.2210 SIGN
yes 6.1.2214 SM/REM
yes 6.1.2216 SOURCE
yes 6.1.2220 SPACE
yes 6.1.2230 SPACES
yes 6.1.2250 STATE
yes 6.1.2260 SWAP
yes 6.1.2270 THEN
yes 6.1.2310 TYPE
yes 6.1.2320 U.
yes 6.1.2340 U<
yes 6.1.2360 UM*
yes 6.1.2370 UM/MOD
yes 6.1.2380 UNLOOP
yes 6.1.2390 UNTIL
yes 6.1.2410 VARIABLE
yes 6.1.2430 WHILE
yes 6.1.2450 WORD
yes 6.1.2490 XOR
yes 6.1.2500 [
yes 6.1.2510 [']
yes 6.1.2520 [CHAR]
yes 6.1.2540 ]
6.2 Core extension words
yes 6.2.0200 .(
6.2.0210 .R
yes 6.2.0260 0<>
yes 6.2.0280 0>
yes 6.2.0340 2>R
yes 6.2.0410 2R>
yes 6.2.0415 2R@
yes 6.2.0455 :NONAME
yes 6.2.0500 <>
yes 6.2.0620 ?DO
yes 6.2.0698 ACTION-OF
yes 6.2.0700 AGAIN
yes 6.2.0825 BUFFER:
yes 6.2.0855 C"
yes 6.2.0873 CASE
yes 6.2.0945 COMPILE,
yes 6.2.1173 DEFER
yes 6.2.1175 DEFER!
yes 6.2.1177 DEFER@
yes 6.2.1342 ENDCASE
yes 6.2.1343 ENDOF
yes 6.2.1350 ERASE
yes 6.2.1485 FALSE
yes 6.2.1660 HEX
6.2.1675 HOLDS
yes 6.2.1725 IS
yes 6.2.1850 MARKER
yes 6.2.1930 NIP
yes 6.2.1950 OF
6.2.2000 PAD
6.2.2008 PARSE
6.2.2020 PARSE-NAME
yes 6.2.2030 PICK
6.2.2125 REFILL
6.2.2148 RESTORE-INPUT
yes 6.2.2150 ROLL
6.2.2266 S\"
6.2.2182 SAVE-INPUT
6.2.2218 SOURCE-ID
yes 6.2.2295 TO
yes 6.2.2298 TRUE
yes 6.2.2300 TUCK
6.2.2330 U.R
6.2.2350 U>
6.2.2395 UNUSED
yes 6.2.2405 VALUE
6.2.2440 WITHIN
6.2.2530 [COMPILE]
yes 6.2.2535 \

@ -1,6 +1,8 @@
-1 constant true
0 constant false
: unused 1 sys ;
: hex 16 base ! ;
: nip swap drop ;
@ -8,42 +10,68 @@
: 0> 0 > ;
: 0<> 0= 0= ;
: u> swap u< ;
: within over - >r - r> swap u> ;
: 2r@ ['] r> , ['] r> , ['] 2dup , ['] >r , ['] >r , ['] swap , ; imm
: compile, postpone literal postpone execute ;
: \ _source @ >in @ +
begin dup c@ while 0 over c! char+ repeat drop ; imm
: again postpone repeat ; imm
: ?do ['] 2dup , ['] _lit , here 0 , ['] >r , ['] = , postpone if
['] 2drop , postpone 2r> ['] drop , ['] >r , ['] exit ,
postpone then postpone 2>r here ; imm
: .( [char] ) word count type ; imm
: c" state @ if ['] _jmp , here 0 , then
[char] " word
state @ 0= if exit then
dup count nip allot
here rot !
postpone literal ; imm
: buffer: create allot ;
: value constant ;
: to ' 4 cells + state @ if postpone literal ['] ! , else ! then ; imm
: defer create does> @ execute ;
: defer@ >body @ ;
: defer! >body ! ;
: is state @ if postpone ['] postpone defer! else ' defer! then ; imm
: compile, postpone literal postpone execute ;
: [compile] bl word find -1 = if , else compile, then ; imm
: \ _source @ >in @ +
begin dup c@ while 0 over c! char+ repeat drop ; imm
: again postpone repeat ; imm
: ?do ['] _lit , here 0 , ['] >r , ['] 2dup , postpone 2>r
['] = , postpone if postpone leave postpone then
here ; imm
: .( [char] ) word count type ; imm
: c" state @ if ['] _jmp , here 0 , then
[char] " here char+ begin
key dup 3 pick <> while
over c! char+ repeat drop
swap drop here - here c! here
state @ 0= if exit then
dup count nip 1+ allot
here rot !
postpone literal ; imm
: buffer: create allot ;
: value constant ;
: to ' 4 cells + state @ if postpone literal ['] ! , else ! then ; imm
: defer create ['] exit , does> @ execute ;
: defer@ >body @ ;
: defer! >body ! ;
: is state @ if postpone ['] postpone defer! else ' defer! then ; imm
: action-of state @ if postpone ['] postpone defer@ else ' defer@ then ; imm
: erase begin dup 0 > while swap 0 over ! 1+ swap 1- repeat ;
: erase 0 fill ;
: roll dup if swap >r 1- recurse r> swap exit then drop ;
: marker create _latest @ , here , does>
dup @ _latest ! cell+ @ here swap - allot ;
: marker here _latest @ create , , does>
dup @ _latest ! cell+ @ here - allot ;
: case 0 ; imm
: of ['] over , ['] = , postpone if ['] drop , ; imm
: endof postpone else ; imm
: endcase ['] drop , begin ?dup while postpone then repeat ; imm
: holds begin dup while 1- 2dup + c@ hold repeat 2drop ;
: .r over dup 0< if 1 else 0 then
begin 1+ swap base @ / dup 0<> while swap repeat
drop - spaces . ;
: u.r over 0 begin 1+ swap 0 base @ _/ dup 0<> while swap repeat
drop - spaces u. ;
( WORD uses HERE and must be at least 33 characters. )
: pad here [ 50 chars ] literal + align ;
: parse here dup >r swap begin
key? if key else dup then 2dup <> while
rot dup >r c! r> char+ swap repeat
2drop r> tuck - ;
: case ['] _lit , 1 here 0 , ['] drop , ; imm
: of ['] over , ['] = , postpone if ; imm
: endof ['] _jmp , here >r 0 , postpone then
swap 1+ swap r> tuck ! ; imm
: endcase swap 0 do dup @ swap here swap ! loop drop ['] drop , ; imm
: source-id _source @ _begin < if 0 else -1 then ;

@ -6,38 +6,50 @@
: cell+ 2 + ;
: cells 2 * ;
: char+ 1 + ;
: chars ;
: . 0 sys ;
: u. 1 sys ;
: emit 2 sys ;
: 1+ 1 + ;
: 1- 1 - ;
: over 1 pick ;
: rot >r swap r> swap ;
: -rot rot rot ;
: ' _' drop ;
: ! 1 _! ;
: @ 1 _@ ;
: +! dup >r swap r> @ + swap ! ;
: base 0 ;
: here 1 cells @ ;
: allot 1 cells +! ;
: _latest 2 cells ;
: imm _latest @ dup @ 1 5 << | swap ! ;
: immediate imm ;
: state 3 cells ;
: _source 4 cells ;
: _sourceu 5 cells ;
: >in 6 cells ;
: , here ! 1 cells allot ;
: [ 0 3 cells ! ; imm
: ] 1 3 cells ! ;
: [ 0 state ! ; imm
: ] 1 state ! ;
: , 1 cells dup >r @ ! r> dup +! ;
: literal [ ' _lit dup , , ] , , ; imm
: ['] ' [ ' literal , ] ; imm
: base 0 ;
: here [ 1 cells ] literal @ ;
: allot [ 1 cells ] literal +! ;
: state [ 3 cells ] literal ;
: _compxt [ 4 cells ] literal ;
: _source [ 5 cells ] literal ;
: _sourceu [ 6 cells ] literal ;
: >in [ 7 cells ] literal ;
: _begin [ 8 cells 80 chars + ] literal ;
: c! 0 _! ;
: c@ 0 _@ ;
: c, here c! 1 allot ;
: if ['] _jmp0 , here 0 , ; imm
: then here swap ! ; imm
: else ['] _jmp , here 0 , swap here swap ! ; imm
@ -46,21 +58,11 @@
1 = swap ['] _lit , , if ['] execute ,
else ['] , , then ; imm
: over 1 pick ;
: rot >r swap r> swap ;
: -rot rot rot ;
: 2drop drop drop ;
: 2dup over over ;
: 2over 3 pick 3 pick ;
: 2swap rot >r rot r> ;
: c! 0 _! ;
: c@ 0 _@ ;
: c, here c! 1 allot ;
: char+ 1+ ;
: chars ;
: decimal 10 base ! ;
: 2r> ['] r> , ['] r> , ['] swap , ; imm
@ -83,22 +85,23 @@
: do ['] _lit , here 0 , ['] >r , postpone 2>r here ; imm
: unloop postpone 2r> ['] 2drop , ['] r> , ['] drop , ; imm
: leave postpone 2r> ['] 2drop , postpone 2r>
['] drop , ['] >r , ['] exit , ; imm
: +loop postpone 2r> ['] 2dup , ['] swap , ['] < , ['] >r ,
['] rot , ['] + , ['] 2dup , ['] swap , ['] < ,
['] r> , ['] ^ , ['] -rot ,
postpone 2>r ['] _jmp0 , ,
postpone unloop here swap ! ; imm
: leave postpone 2r> ['] 2drop , ['] exit , ; imm
: +loop ['] r> , ['] 2dup , ['] + ,
postpone r@ ['] swap , ['] >r ,
['] - , ['] 2dup , ['] + , ['] over , ['] ^ ,
['] rot , ['] rot , ['] ^ , ['] & , ['] _lit , 0 ,
['] < , ['] _jmp0 , ,
postpone unloop here 1 cells - swap ! ; imm
: loop postpone 2r> ['] 1+ , ['] 2dup ,
postpone 2>r ['] = , ['] _jmp0 , ,
postpone unloop here swap ! ; imm
postpone unloop here 1 cells - swap ! ; imm
: i postpone r@ ; imm
: j postpone 2r> ['] r> , postpone r@ ['] swap ,
['] >r , ['] -rot , postpone 2>r ; imm
: align here 1 cells 1- swap over & if 1 cells swap - allot else drop then ;
: aligned dup 1 cells 1- swap over & if 1 cells swap - + else drop then ;
: aligned dup [ 1 cells 1- ] literal swap over & if [ 1 cells ] literal
swap - + else drop then ;
: align here dup aligned swap - allot ;
: and & ;
: or | ;
@ -108,7 +111,7 @@
: invert -1 ^ ;
: mod % ;
: 2* 2 * ;
: _msb 1 1 cells 8 * 1- << ;
: _msb [ 1 1 cells 8 * 1- << ] literal ;
: 2/ dup 1 >> swap 0< if _msb or then ;
: /mod 2dup % -rot / ;
@ -131,22 +134,26 @@
: min 2dup <= if drop else swap drop then ;
: max 2dup <= if swap drop else drop then ;
: source _source @ 0 begin 2dup + c@ while char+ repeat ;
: source _source @ _sourceu @ ;
: key _source @ >in @ +
begin dup c@ 0 = while _in repeat
c@ 1 >in +! ;
: key? _source @ >in @ + c@ 0 <> ;
: word here dup >r char+ >r
begin key? if key 2dup <> else 0 0 then while
r> swap over c! char+ >r repeat
2drop r> r> swap over - 1- over c! ;
: word begin key? if key else -1 then 2dup <> until
key? 0= if 2drop 0 here c! here exit then
here begin char+ swap over c! swap
key? if key else dup then
2dup <> while rot repeat
2drop here - here c! here ;
: count dup char+ swap c@ ;
: char bl word char+ c@ ;
: char 0 here char+ c! bl word char+ c@ ;
: [char] char postpone literal ; imm
: ( begin [char] ) key <> while repeat ; imm
: type begin dup 0 > while swap dup c@ emit char+ swap 1- repeat 2drop ;
: _type >r begin dup 0 > while
swap dup c@ r@ execute char+ swap 1- repeat 2drop r> drop ;
: type [ ' emit ] literal _type ;
: s" state @ if ['] _jmp , here 0 , then
[char] " word count
state @ 0= if exit then
@ -155,21 +162,19 @@
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
['] _lit , here 3 cells + , ['] exit dup , ,
dup @ 31 & over _latest @ - 6 << or over ! _latest ! ;
: _does> _latest @ dup @ 31 & + cell+ aligned 2 cells +
['] _jmp over ! cell+
r@ 1 cells - @ swap ! ;
: does> ['] _jmp , here 2 cells + dup , 2 cells + ,
['] _does> , ['] exit , ; imm
: :noname here dup _compxt ! 0 , here swap ] ;
: create : here [ 4 cells ] literal + postpone literal postpone ; 0 , ;
: >body cell+ @ ;
: variable create 1 cells allot ;
: _does> >r _latest @ dup @ 31 & + cell+ aligned [ 2 cells ] literal +
['] _jmp over ! cell+ r> cell+ swap ! ;
: does> state @ if
['] _lit , here 2 cells + , ['] _does> , ['] exit , else
here dup _does> dup _compxt ! 0 , ] then ; imm
: variable create [ 1 cells ] literal allot ;
: constant create , does> @ ;
: quit begin _rdepth 1 > while r> drop repeat postpone [ ;
@ -178,7 +183,7 @@
postpone if ['] type , ['] abort ,
postpone else ['] 2drop , postpone then ; imm
: recurse depth 1- pick dup @ 31 & + cell+ aligned , ; imm
: recurse _compxt @ dup @ 31 & + cell+ aligned , ; imm
: move dup 0 <= if drop 2drop exit then
>r 2dup < r> swap if
@ -195,9 +200,7 @@
: accept over >r begin dup 0 > while
key dup 32 < if 2drop 0
else dup emit rot 2dup c! char+ swap drop swap 1- then
repeat drop r> - 1 chars / ;
: :noname 0 , here ] ;
repeat drop r> - [ 1 chars ] literal / ;
: evaluate _source @ >r _sourceu @ >r >in @ >r
0 >in ! _sourceu ! _source ! _ev
@ -217,3 +220,15 @@
>r dup c@ swap >r base @ swap
dup _isdigit - _uma
r> char+ r> 1- repeat ;
: <# 40 here c! ;
: #> 2drop here dup c@ + 40 here c@ - ;
: hold -1 here +! here dup c@ + c! ;
: # base @
>r 0 i um/mod r> swap >r um/mod r>
rot 9 over <
if 7 + then 48 + hold ;
: #s begin # 2dup or 0= until ;
: sign 0< if [char] - hold then ;
: u. 0 <# bl hold #s #> type ;

@ -0,0 +1,97 @@
: vector! 10 sys ;
: byte! 11 sys ;
: byte@ 12 sys ;
: reg! 13 sys ;
: reg@ 14 sys ;
: sr+ 15 sys ;
: sr- 16 sys ;
: lpm-exit 17 sys ;
: reg [ ' reg@ ' reg! ] literal literal ;
: byte [ ' byte@ ' byte! ] literal literal ;
: set ( b r reg/byte -- )
>r over r> execute >r rot r> | -rot execute ;
: clear ( b r reg/byte -- )
>r over r> execute >r rot invert r> & -rot execute ;
: toggle ( b r reg/byte -- )
>r over r> execute >r rot r> ^ -rot execute ;
create _outs p1out , p2out , p3out , p4out , p5out , p6out ,
create _ins p1in , p2in , p3in , p4in , p5in , p6in ,
create _dirs p1dir , p2dir , p3dir , p4dir , p5dir , p6dir ,
1 constant output
0 constant input
: pin-mode ( output? pin port -- )
rot >r cells _dirs + @ byte r> if set else clear then ;
: pin-set ( high? pin port -- )
rot >r cells _outs + @ byte r> if set else clear then ;
: pin-get ( pin port -- high? )
cells _ins + @ byte@ swap and 0 > ;
: analog-init
adcon adcsht_2 or adcctl0 reg set
adcshp adcctl1 reg set
adcres adcctl2 reg clear
adcres_2 adcctl2 reg set
adcie0 adcie reg set ;
: rtc-init
rtcps__10 rtcctl reg! ;
: ms ( u -- )
rtcmod reg!
rtcss_3 rtcsr or rtcctl reg set
begin rtciv reg@ 0<> until
rtc-init ;
: D0 bit5 1 ;
: D1 bit6 1 ;
: D2 bit1 2 ;
: D3 bit4 1 ;
: D4 bit7 2 ;
: D5 bit0 3 ;
: D6 bit1 3 ;
: D7 bit7 3 ;
: D8 bit6 3 ;
: D9 bit5 3 ;
: D10 bit4 4 ;
: D11 bit2 2 ;
: D12 bit6 2 ;
: D13 bit5 2 ;
: A0 bit0 0 ;
: A1 bit1 0 ;
: A2 bit5 0 ;
: A3 bit6 0 ;
: A4 bit2 0 ;
: A5 bit3 0 ;
: AREF bit4 0 ;
: pin-analog
drop
dup p1sel0 reg set
p1sel1 reg set ;
: analog-get
drop 0 begin
swap 2/ dup 0<> while
swap 1+ repeat
drop adcmctl0 reg!
adcenc adcsc or adcctl0 reg set
adcmem0 reg@ ;
: LED1R bit1 5 ;
: LED1G bit0 5 ;
: LED1B bit2 5 ;
: LED2R bit6 4 ;
: LED2G bit5 4 ;
: LED2B bit7 4 ;
: SW2 bit3 1 ;
: SW3 bit4 2 ;

File diff suppressed because it is too large Load Diff

@ -1,66 +0,0 @@
\ From: John Hayes S1I
\ Subject: tester.fr
\ Date: Mon, 27 Nov 95 13:10:09 PST
\ (C) 1995 JOHNS HOPKINS UNIVERSITY / APPLIED PHYSICS LABORATORY
\ MAY BE DISTRIBUTED FREELY AS LONG AS THIS COPYRIGHT NOTICE REMAINS.
\ VERSION 1.2
\ 24/11/2015 Replaced Core Ext word <> with = 0=
\ 31/3/2015 Variable #ERRORS added and incremented for each error reported.
\ 22/1/09 The words { and } have been changed to T{ and }T respectively to
\ agree with the Forth 200X file ttester.fs. This avoids clashes with
\ locals using { ... } and the FSL use of }
HEX
\ SET THE FOLLOWING FLAG TO TRUE FOR MORE VERBOSE OUTPUT; THIS MAY
\ ALLOW YOU TO TELL WHICH TEST CAUSED YOUR SYSTEM TO HANG.
VARIABLE VERBOSE
FALSE VERBOSE !
\ TRUE VERBOSE !
: EMPTY-STACK \ ( ... -- ) EMPTY STACK: HANDLES UNDERFLOWED STACK TOO.
DEPTH ?DUP IF DUP 0< IF NEGATE 0 DO 0 LOOP ELSE 0 DO DROP LOOP THEN THEN ;
VARIABLE #ERRORS 0 #ERRORS !
: ERROR \ ( C-ADDR U -- ) DISPLAY AN ERROR MESSAGE FOLLOWED BY
\ THE LINE THAT HAD THE ERROR.
CR TYPE SOURCE TYPE \ DISPLAY LINE CORRESPONDING TO ERROR
EMPTY-STACK \ THROW AWAY EVERY THING ELSE
#ERRORS @ 1 + #ERRORS !
\ QUIT \ *** Uncomment this line to QUIT on an error
;
VARIABLE ACTUAL-DEPTH \ STACK RECORD
CREATE ACTUAL-RESULTS 20 CELLS ALLOT
: T{ \ ( -- ) SYNTACTIC SUGAR.
;
: -> \ ( ... -- ) RECORD DEPTH AND CONTENT OF STACK.
DEPTH DUP ACTUAL-DEPTH ! \ RECORD DEPTH
?DUP IF \ IF THERE IS SOMETHING ON STACK
0 DO ACTUAL-RESULTS I CELLS + ! LOOP \ SAVE THEM
THEN ;
: }T \ ( ... -- ) COMPARE STACK (EXPECTED) CONTENTS WITH SAVED
\ (ACTUAL) CONTENTS.
DEPTH ACTUAL-DEPTH @ = IF \ IF DEPTHS MATCH
DEPTH ?DUP IF \ IF THERE IS SOMETHING ON THE STACK
0 DO \ FOR EACH STACK ITEM
ACTUAL-RESULTS I CELLS + @ \ COMPARE ACTUAL WITH EXPECTED
= 0= IF S" INCORRECT RESULT: " ERROR LEAVE THEN
LOOP
THEN
ELSE \ DEPTH MISMATCH
S" WRONG NUMBER OF RESULTS: " ERROR
THEN ;
: TESTING \ ( -- ) TALKING COMMENT.
SOURCE VERBOSE @
IF DUP >R TYPE CR R> >IN !
ELSE >IN ! DROP [CHAR] * EMIT
THEN ;

@ -0,0 +1,13 @@
: .s depth dup 0 ?do dup i - pick . loop drop ;
: ? @ . ;
: dump hex 0 do i cells over + @ s>d <# # # # # bl hold #> type loop
drop decimal ;
: words _latest @ begin
dup @ dup 31 &
2 pick cell+ \ lt l len ws
2 pick 6 >> 1023 < if \ lt l len ws
rot 6 >> else \ lt len ws adv
>r cell+ rot drop r> @ then
-rot swap type space \ lt adv
over _begin <> while - repeat 2drop ;

@ -0,0 +1,7 @@
#include "config.hpp"
#include "corewords.hpp"
#include "ctype.hpp"
#include "dictionary.hpp"
#include "parser.hpp"
#include "state.hpp"
#include "types.hpp"

@ -0,0 +1,5 @@
#ifndef ALEE_MSP430_HOST
#define LIBALEE_SECTION
#else
#define LIBALEE_SECTION __attribute__((section(".libalee")))
#endif

@ -16,216 +16,187 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "corewords.hpp"
#include "parser.hpp"
#include "alee.hpp"
#include <cstring>
#include <utility>
Word getword(State& state)
{
auto word = state.dict.input();
while (word.size() == 0) {
state.input(state);
word = state.dict.input();
}
return word;
}
void newdef(State& state, Word word)
{
auto& dict = state.dict;
auto addr = dict.alignhere();
dict.addDefinition(word);
state.push(addr);
};
void find(State& state, Word word)
{
if (auto j = state.dict.find(word); j > 0) {
state.push(state.dict.getexec(j));
auto imm = state.dict.read(j) & Dictionary::Immediate;
state.push(imm ? 1 : -1);
} else if (auto i = CoreWords::findi(state, word); i >= 0) {
state.push(i);
state.push(i == CoreWords::Semicolon ? 1 : -1);
} else {
state.push(0);
state.push(0);
}
}
static void find(State&, Word);
static DoubleCell popd(State&);
static void pushd(State&, DoubleCell);
LIBALEE_SECTION
void CoreWords::run(Cell ins, State& state)
{
Cell cell;
DoubleCell dcell;
Addr index = ins;
auto popd = [](State& s) {
DoubleCell dcell = s.pop();
dcell <<= sizeof(Cell) * 8;
dcell |= static_cast<Addr>(s.pop());
return dcell;
};
auto pushd = [](State& s, DoubleCell d) {
s.push(static_cast<Cell>(d));
s.push(static_cast<Cell>(d >> (sizeof(Cell) * 8)));
};
auto& ip = state.ip();
execute:
if (index >= Dictionary::Begin) {
// must be calling a defined subroutine
state.pushr(state.ip);
state.ip = index;
state.pushr(ip);
ip = index;
return;
} else switch (index) {
case 0: // _lit
case token("_lit"): // Execution semantics of `literal`.
state.push(state.beyondip());
break;
case 1: // drop
case token("drop"):
state.pop();
break;
case 2: // dup
case token("dup"):
state.push(state.top());
break;
case 3: // swap
case token("swap"):
std::swap(state.top(), state.pick(1));
break;
case 4: // pick
case token("pick"):
state.push(state.pick(state.pop()));
break;
case 5: // sys
case token("sys"): // Calls user-defined "system" handler.
user_sys(state);
break;
case 6: // add
case token("+"):
cell = state.pop();
state.top() += cell;
break;
case 7: // sub
case token("-"):
cell = state.pop();
state.top() -= cell;
break;
case 8: // mul ( n n -- d )
case token("m*"): // ( n n -- d )
cell = state.pop();
dcell = state.pop() * cell;
pushd(state, dcell);
break;
case 9: // div ( d n -- n )
case token("_/"): // ( d n -- n )
cell = state.pop();
dcell = popd(state);
state.push(static_cast<Cell>(dcell / cell));
break;
case 10: // mod ( d n -- n )
case token("_%"): // ( d n -- n )
cell = state.pop();
dcell = popd(state);
state.push(static_cast<Cell>(dcell % cell));
break;
case 11: // peek
case token("_@"): // ( addr cell? -- n )
if (state.pop())
state.push(state.dict.read(state.pop()));
else
state.push(state.dict.readbyte(state.pop()));
break;
case 12: // poke
case token("_!"): // ( n addr cell? -- )
cell = state.pop();
if (auto addr = state.pop(); cell)
state.dict.write(addr, state.pop());
else
state.dict.writebyte(addr, state.pop() & 0xFFu);
break;
case 13: // pushr
case token(">r"):
state.pushr(state.pop());
break;
case 14: // popr
case token("r>"):
state.push(state.popr());
break;
case 15: // equal
case token("="):
cell = state.pop();
state.top() = state.top() == cell ? -1 : 0;
break;
case 16: // lt
case token("<"):
cell = state.pop();
state.top() = state.top() < cell ? -1 : 0;
break;
case 17: // and
case token("&"):
cell = state.pop();
state.top() &= cell;
break;
case 18: // or
case token("|"):
cell = state.pop();
state.top() |= cell;
break;
case 19: // xor
case token("^"):
cell = state.pop();
state.top() ^= cell;
break;
case 20: // shl
case token("<<"):
cell = state.pop();
reinterpret_cast<Addr&>(state.top()) <<= static_cast<Addr>(cell);
break;
case 21: // shr
case token(">>"):
cell = state.pop();
reinterpret_cast<Addr&>(state.top()) >>= static_cast<Addr>(cell);
break;
case 22: // colon
newdef(state, getword(state));
case token(":"): // Begins definition/compilation of new word.
state.push(state.dict.alignhere());
state.dict.write(Dictionary::CompToken, state.top());
while (!state.dict.hasInput())
state.input();
state.dict.addDefinition(state.dict.input());
state.compiling(true);
break;
case 23: // tick
find(state, getword(state));
case token("_'"): // Collects input word and finds execution token.
while (!state.dict.hasInput())
state.input();
find(state, state.dict.input());
break;
case 24: // execute
case token("execute"):
index = state.pop();
goto execute;
case 25: // exit
state.ip = state.popr();
if (state.ip == 0)
std::longjmp(state.jmpbuf, static_cast<int>(Error::exit));
case token("exit"):
ip = state.popr();
state.verify(ip != 0, Error::exit);
break;
case 26: // semic
state.dict.add(findi("exit"));
case token(";"): // Concludes word definition.
state.dict.add(token("exit"));
state.compiling(false);
cell = state.pop();
dcell = (cell - state.dict.latest()) << 6;
dcell = cell - state.dict.latest();
if (dcell >= Dictionary::MaxDistance) {
// Large distance to previous entry: store in dedicated cell.
state.dict.write(static_cast<Addr>(cell) + sizeof(Cell),
static_cast<Cell>(dcell));
dcell = Dictionary::MaxDistance;
}
state.dict.write(cell,
(state.dict.read(cell) & 0x1F) | static_cast<Cell>(dcell));
(state.dict.read(cell) & 0x1F) | static_cast<Cell>(dcell << 6));
state.dict.latest(cell);
break;
case 27: // _jmp0
case token("_jmp0"): // Jump if popped value equals zero.
if (state.pop()) {
state.beyondip();
break;
}
[[fallthrough]];
case 28: // _jmp
state.ip = state.beyondip();
case token("_jmp"): // Unconditional jump.
ip = state.beyondip();
return;
case 29: // depth
case token("depth"):
state.push(static_cast<Cell>(state.size()));
break;
case 30: // _rdepth
case token("_rdepth"):
state.push(static_cast<Cell>(state.rsize()));
break;
case 31: // _in
state.input(state);
case token("_in"): // Fetches more input from the user input source.
state.input();
break;
case 32: // _ex
case token("_ev"): // Evaluates words from current input source.
{
const auto st = state.save();
state.ip = 0;
ip = 0;
Parser::parseSource(state);
state.load(st);
}
break;
case 33: // find
case token("find"):
cell = state.pop();
find(state,
Word::fromLength(static_cast<Addr>(cell + 1),
state.dict.readbyte(cell)));
break;
case 34: // _uma
case token("_uma"): // ( d u u -- d ): Unsigned multiply-add.
{
const auto plus = state.pop();
cell = state.pop();
@ -235,12 +206,12 @@ execute:
pushd(state, dcell);
}
break;
case 35: // u<
case token("u<"):
cell = state.pop();
state.top() = static_cast<Addr>(state.top()) <
static_cast<Addr>(cell) ? -1 : 0;
break;
case 36: // um/mod
case token("um/mod"):
cell = state.pop();
dcell = popd(state);
@ -251,43 +222,48 @@ execute:
static_cast<DoubleAddr>(dcell) /
static_cast<Addr>(cell)));
break;
default:
default: // Compacted literals (WordCount <= ins < Begin).
state.push(ins - WordCount);
break;
}
state.ip += sizeof(Cell);
ip += sizeof(Cell);
}
template<typename Iter>
Cell findi(Iter it, std::size_t size)
LIBALEE_SECTION
Cell CoreWords::findi(State& state, Word word)
{
auto ptr = CoreWords::wordsarr;
Cell wordsi = 0;
while (ptr < CoreWords::wordsarr + sizeof(CoreWords::wordsarr)) {
auto end = ptr;
while (*end)
++end;
return findi(word.begin(&state.dict), word.size());
}
std::size_t wordsize = end - ptr;
if (wordsize == size && Dictionary::equal(ptr, end, it))
return wordsi;
LIBALEE_SECTION
void find(State& state, Word word)
{
Cell tok = 0;
Cell imm = 0;
++wordsi;
ptr = end + 1;
if (auto j = state.dict.find(word); j > 0) {
tok = state.dict.getexec(j);
imm = (state.dict.read(j) & Dictionary::Immediate) ? 1 : -1;
} else if (tok = CoreWords::findi(state, word); tok >= 0) {
imm = (tok == CoreWords::token(";")) ? 1 : -1;
}
return -1;
state.push(tok);
state.push(imm);
}
Cell CoreWords::findi(const char *word)
DoubleCell popd(State& s)
{
return ::findi(word, strlen(word));
DoubleCell dcell = s.pop();
dcell <<= sizeof(Cell) * 8;
dcell |= static_cast<Addr>(s.pop());
return dcell;
}
Cell CoreWords::findi(State& state, Word word)
void pushd(State& s, DoubleCell d)
{
return ::findi(word.begin(&state.dict), word.size());
s.push(static_cast<Cell>(d));
s.push(static_cast<Cell>(d >> (sizeof(Cell) * 8)));
}

@ -1,51 +1,78 @@
/**
* Alee Forth: A portable and concise Forth implementation in modern C++.
* Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//
/// @file corewords.hpp
/// @brief Manages the fundamental word-set and its execution.
//
// Alee Forth: A portable and concise Forth implementation in modern C++.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef ALEEFORTH_COREWORDS_HPP
#define ALEEFORTH_COREWORDS_HPP
#include "config.hpp"
#include "dictionary.hpp"
#include "types.hpp"
#include "state.hpp"
#include <algorithm>
class State;
/**
* To be implemented by the user, this function is called when the `sys` word
* is executed.
* @param state Current execution state object.
*/
void user_sys(State&);
void user_sys(State& state);
/**
* @class CoreWords
* @brief Provides the fundamental word-set and manages its execution.
*/
class CoreWords
{
public:
constexpr static Cell WordCount = 37;
constexpr static Cell Semicolon = 26;
/**
* Searches for the token/index of the given word if it is part of the
* fundamental word-set.
* @param state Current execution state object.
* @param word Word (stored in state's dictionary memory) to look up.
* @return The token/index of the word or -1 if not found.
*/
static Cell findi(State& state, Word word);
/**
* Finds execution token that corresponds to the given word.
* Returns -1 if not found.
* Looks up the token/index of the given fundamental word.
* Primarily used for compile-time lookup.
* @param word The word to look up.
* @return The token/index of the word or -1 if not found.
*/
static Cell findi(const char *);
static Cell findi(State&, Word);
consteval static Cell token(const char *word) {
return findi(word, strlen(word));
}
/**
* Executes the given CoreWord execution token using the given state.
* Executes the given execution token using the given state.
* @param token Any valid execution token (word, fundamental, constant...).
* @param state The state object to execute with.
*/
static void run(Cell, State&);
static void run(Cell token, State& state);
/**
* String lookup table for the fundamental word-set.
* This also determines the opcode (index) of these words.
*/
constexpr static char wordsarr[] =
"_lit\0drop\0dup\0swap\0pick\0sys\0"
"+\0-\0m*\0_/\0_%\0"
@ -55,6 +82,37 @@ public:
"exit\0;\0_jmp0\0_jmp\0"
"depth\0_rdepth\0_in\0_ev\0find\0"
"_uma\0u<\0um/mod\0";
/**
* Count of total fundamental words.
*/
constexpr static Cell WordCount = [] {
return std::count(wordsarr, wordsarr + sizeof(wordsarr), '\0'); }();
private:
/**
* Generic implementation of findi(). Private; use public implementations.
* @param it Beginning iterator of the word to search for.
* @param size Size of the searched-for word i.e. end == it + size.
* @return The token/index of the word or -1 if not found.
*/
template<typename Iter>
LIBALEE_SECTION
constexpr static Cell findi(Iter it, std::size_t size)
{
const char *ptr = CoreWords::wordsarr;
for (Cell wordsi = 0; wordsi < WordCount; ++wordsi) {
std::size_t wordsize = strlen(ptr);
if (wordsize == size && Dictionary::equal(ptr, ptr + wordsize, it))
return wordsi;
ptr += wordsize + 1;
}
return -1;
}
};
#endif // ALEEFORTH_COREWORDS_HPP

@ -1,36 +0,0 @@
/**
* Alee Forth: A portable and concise Forth implementation in modern C++.
* Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ctype.hpp"
bool isspace(uint8_t c) {
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
bool isdigit(uint8_t c) {
return c >= '0' && c <= '9';
}
bool isalpha(uint8_t c) {
return isupper(c) || (c >= 'a' && c <= 'z');
}
bool isupper(uint8_t c) {
return c >= 'A' && c <= 'Z';
}

@ -1,34 +1,59 @@
/**
* Alee Forth: A portable and concise Forth implementation in modern C++.
* Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//
/// @file ctype.hpp
/// @brief Simple implementations of character comparison functions.
//
// Alee Forth: A portable and concise Forth implementation in modern C++.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef ALEEFORTH_CTYPE_HPP
#define ALEEFORTH_CTYPE_HPP
/**
* We implement our own character comparison functions to keep them lean.
*/
#include <cstdint>
bool isspace(uint8_t);
bool isdigit(uint8_t);
bool isalpha(uint8_t);
bool isupper(uint8_t);
/** Determines the length of a null-terminated string. */
constexpr inline unsigned strlen(const char * const s) {
unsigned i = 0;
while (s[i]) i++;
return i;
}
/** Tests if given character represents whitespace. */
constexpr inline bool isspace(uint8_t c) {
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
/** Tests if given character is a numerical digit. */
constexpr inline bool isdigit(uint8_t c) {
return c >= '0' && c <= '9';
}
/** Tests if given character is an uppercase letter. */
constexpr inline bool isupper(uint8_t c) {
return c >= 'A' && c <= 'Z';
}
/** Tests if given character is a lowercase letter. */
constexpr inline bool islower(uint8_t c) {
return c >= 'a' && c <= 'z';
}
/** Tests if given character is a letter. */
constexpr inline bool isalpha(uint8_t c) {
return isupper(c) || (c >= 'a' && c <= 'z');
}
#endif // ALEEFORTH_CTYPE_HPP

@ -16,11 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ctype.hpp"
#include "dictionary.hpp"
#include <cstring>
#include "alee.hpp"
LIBALEE_SECTION
void Dictionary::initialize()
{
write(Base, 10);
@ -30,6 +28,7 @@ void Dictionary::initialize()
write(Source, Input + sizeof(Cell));
}
LIBALEE_SECTION
Addr Dictionary::allot(Cell amount) noexcept
{
Addr old = here();
@ -38,119 +37,157 @@ Addr Dictionary::allot(Cell amount) noexcept
if (neww < capacity()) {
write(Here, static_cast<Addr>(neww));
} else {
// TODO
// TODO how to handle allot failure? Error code?
}
return old;
}
LIBALEE_SECTION
void Dictionary::add(Cell value) noexcept
{
write(allot(sizeof(Cell)), value);
}
Addr Dictionary::aligned(Addr addr) const noexcept
LIBALEE_SECTION
Addr Dictionary::aligned(Addr addr)
{
Addr unaligned = addr & (sizeof(Cell) - sizeof(uint8_t));
if (unaligned) {
addr += sizeof(Cell);
addr -= unaligned;
}
return addr;
return (addr + (sizeof(Cell) - 1)) & ~(sizeof(Cell) - 1);
}
LIBALEE_SECTION
Addr Dictionary::alignhere() noexcept
{
here(aligned(here()));
return here();
}
LIBALEE_SECTION
void Dictionary::addDefinition(Word word) noexcept
{
Cell wsize = word.size();
add(wsize);
for (auto w = word.start; w != word.wend; ++w)
writebyte(allot(1), readbyte(w));
if (alignhere() - latest() >= MaxDistance)
add(0);
auto addr = allot(wsize);
auto it = word.begin(this);
const auto end = word.end(this);
while (it != end)
writebyte(addr++, *it++);
alignhere();
}
LIBALEE_SECTION
Addr Dictionary::find(Word word) noexcept
{
Addr lt = latest();
for (;;) {
const Addr l = read(lt);
const Addr len = l & 0x1F;
Word lw;
lw.start = lt + sizeof(Cell);
lw.wend = lw.start + len;
if (equal(word, lw))
return lt;
else if (lt == Begin)
break;
else
lt -= l >> 6;
if ((l >> 6) < MaxDistance) {
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<Addr>(read(lt + sizeof(Cell)));
}
}
return 0;
}
LIBALEE_SECTION
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) == MaxDistance)
addr += sizeof(Cell);
addr += len;
return aligned(addr);
}
LIBALEE_SECTION
bool Dictionary::hasInput() const noexcept
{
const Addr src = read(Dictionary::Source);
const Addr end = read(Dictionary::SourceLen);
auto idx = static_cast<uint8_t>(read(Dictionary::Input));
while (idx < end) {
auto ch = readbyte(src + idx);
if (ch == '\0') {
break;
} else if (!isspace(ch)) {
return true;
}
++idx;
}
return false;
}
LIBALEE_SECTION
Word Dictionary::input() noexcept
{
const Addr src = read(Dictionary::Source);
const Addr end = read(Dictionary::SourceLen);
uint8_t idx = read(Dictionary::Input) & 0xFFu;
auto idx = static_cast<uint8_t>(read(Dictionary::Input));
Word word;
word.start = src + idx;
word.wend = word.start;
Addr wstart = src + idx;
Addr wend = wstart;
while (idx < end) {
auto ch = readbyte(word.wend);
auto ch = readbyte(wend);
if (isspace(ch)) {
if (word.size() > 0)
if (wend - wstart > 0)
break;
++word.start;
++wstart;
} else if (ch == '\0') {
break;
}
++word.wend;
++wend;
++idx;
}
writebyte(Dictionary::Input, ++idx);
return word;
return Word(wstart, wend);
}
LIBALEE_SECTION
bool Dictionary::equal(Word word, const char *str, unsigned len) const noexcept
{
return word.size() == len &&
equal(word.begin(this), word.end(this), str);
return word.size() == len && equal(word.begin(this), word.end(this), str);
}
LIBALEE_SECTION
bool Dictionary::equal(Word word, Word other) const noexcept
{
return word.size() == other.size() &&
equal(word.begin(this), word.end(this), other.begin(this));
}
bool Dictionary::eqchars(char c1, char c2)
{
return c1 == c2 ||
(isalpha(c1) && isalpha(c2) && (c1 | 32) == (c2 | 32));
return word.size() == other.size() && equal(word.begin(this), word.end(this), other.begin(this));
}

@ -1,136 +1,230 @@
/**
* Alee Forth: A portable and concise Forth implementation in modern C++.
* Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//
/// @file dictionary.hpp
/// @brief Defines the dictionary interface and common functionality.
//
// Alee Forth: A portable and concise Forth implementation in modern C++.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef ALEEFORTH_DICTIONARY_HPP
#define ALEEFORTH_DICTIONARY_HPP
#include "config.hpp"
#include "types.hpp"
#include "ctype.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
/**
* Dictionary entry format:
* - 1 information byte
* bits 0..4: Length of name (L)
* bit 5: Immediate?
* bits 6..15: Distance to next entry (negative)
* - L bytes of name
* - 0+ bytes for address alignment
* - 0+ bytes of entry's data...
* @class Dictionary
* @brief Provides an interface and essential funcitonality for a dictionary.
* @details The core read and write functionality is left virtual so that
* dictionaries can be stored in any medium. So, this class cannot be used
* directly; the programmer must define a dictionary class that inherits this
* one.
*
* Dictionary entry format (for a 16-bit implementation):
* - One information cell:
* - bits 0..4: Length of name
* - bit 5: Set if word is immediate
* - bits 6..15: Distance (backwards) to the next entry
* - If bits 6..15 are all one-bits then "long" distance in following cell.
* - "Length" bytes of name
* - Zero or more bytes for address alignment
* - Zero or more bytes of the definition's contents
*/
class Dictionary
{
public:
/**
* The beginning of the dictionary is used for "internal" variables.
*/
/** Stores Numerical base to use for input/output. */
constexpr static Addr Base = 0;
/** Stores the current `here` address. */
constexpr static Addr Here = sizeof(Cell);
/** Stores the address of the most recently defined word. */
constexpr static Addr Latest = sizeof(Cell) * 2;
/** Stores a boolean indication of compiling state. */
constexpr static Addr Compiling = sizeof(Cell) * 3;
constexpr static Addr Source = sizeof(Cell) * 4;
constexpr static Addr SourceLen = sizeof(Cell) * 5;
constexpr static Addr Input = sizeof(Cell) * 6; // len data...
constexpr static Addr InputCells = 80; // bytes!
constexpr static Addr Begin = sizeof(Cell) * 7 + InputCells;
/** Stores the address of the last execution token determined by colon. */
constexpr static Addr CompToken = sizeof(Cell) * 4;
/** Stores the address of the current interpreter input source. */
constexpr static Addr Source = sizeof(Cell) * 5;
/** Stores the size in bytes of the interpreter input source. */
constexpr static Addr SourceLen = sizeof(Cell) * 6;
/** Stores the dictionary's input buffer (a counted string). */
constexpr static Addr Input = sizeof(Cell) * 7;
/** Stores the size of the dictionary's input buffer in bytes. */
constexpr static Addr InputCells = 80;
/** Stores the dictionary's "beginning" i.e. where new definitions begin. */
constexpr static Addr Begin = sizeof(Cell) * 8 + InputCells;
/** "Immediate" marker bit for a word's definition. */
constexpr static Cell Immediate = (1 << 5);
/** Maximum "short" distance between two definitions. */
constexpr static Cell MaxDistance = (1 << (sizeof(Cell) * 8 - 6)) - 1;
/**
* Dictionary data can be stored on any read-write interface.
* You must create a dictionary class that inherits Dictionary and
* implement these functions. See `memdict.hpp` for a simple block-of-
* memory implementation.
*/
/** Returns the value of the cell at the given address. */
virtual Cell read(Addr) const noexcept = 0;
/** Writes the given value to the cell at the given address. */
virtual void write(Addr, Cell) noexcept = 0;
/** Returns the byte stored at the given address. */
virtual uint8_t readbyte(Addr) const noexcept = 0;
/** Writes the given byte to the given address. */
virtual void writebyte(Addr, uint8_t) noexcept = 0;
/** Returns the total capacity of the dictionary in bytes. */
virtual unsigned long int capacity() const noexcept = 0;
/**
* Does initial dictionary setup, required before use for execution.
* Initializes essential dictionary values.
* Must be called before dictionary use.
*/
void initialize();
/**
* Gets the address stored in `here`.
*/
LIBALEE_SECTION
Addr here() const noexcept { return read(Here); }
/**
* Sets the address stored in `here`.
*/
LIBALEE_SECTION
void here(Addr l) noexcept { write(Here, l); }
/**
* Gets the value of `latest`.
*/
LIBALEE_SECTION
Addr latest() const noexcept { return read(Latest); }
/**
* Sets the value of `latest`.
*/
LIBALEE_SECTION
void latest(Addr l) noexcept { write(Latest, l); }
// Aligns the given address.
Addr aligned(Addr) const noexcept;
// Aligns `here`.
/**
* Aligns the given address to the next Cell boundary if necessary.
* @param addr The address to align.
* @return The resulting aligned address.
*/
static Addr aligned(Addr addr);
/**
* Aligns `here` to the next Cell boundary if necessary.
* @return The new aligned address stored in `here`.
*/
Addr alignhere() noexcept;
// Advances `here` by the given number of bytes.
Addr allot(Cell) noexcept;
// Stores value to `here`, then adds sizeof(Cell) to `here`.
void add(Cell) noexcept;
/**
* Uses add() to store a new definition entry starting at `here`.
* The entry does not become active until a semicolon is executed.
* Allocates memory by returning and then increasing the current `here`.
* @param count The number of bytes to increase `here` by.
* @return The address stored in `here` before the increase.
*/
Addr allot(Cell count) noexcept;
/**
* Stores the given value to `here` then calls allot to "save" that cell.
* @param value The value to store.
* @see allot(Cell)
*/
void add(Cell value) noexcept;
/**
* Stores the beginning of a new word definition in the dictionary.
* The word must eventually have its definition concluded via semicolon.
* @param word The dictionary-stored name of the new word.
*/
void addDefinition(Word) noexcept;
void addDefinition(Word word) noexcept;
/**
* Searches the dictionary for an entry for the given word.
* Returns zero if not found.
* @param word The dictionary-stored word to search for.
* @return The beginning address of the word or zero if not found.
*/
Addr find(Word) noexcept;
Addr find(Word word) noexcept;
/**
* Given the address of a dictionary entry, produces the execution token
* for that entry.
* Produces the execution token for the given dictionary entry.
* @param addr The beginning address of a defined word.
* @return The execution token for the given word.
* @see find(Word)
*/
Addr getexec(Addr) noexcept;
Addr getexec(Addr addr) noexcept;
/**
* Reads the next word from the input buffer.
* Returns an empty word if the buffer is empty or entirely read.
* @return The next word or an empty word if one is not available.
*/
Word input() noexcept;
/**
* Checks if this dictionary's word is equivalent to the given string/size.
* Returns true if the dictionary's input buffer has immediately available
* data.
*/
bool hasInput() const noexcept;
/**
* Checks if the dictionary-stored word is equivalent to the given string.
* @param word Dictionary-stored word to compare against.
* @param str String to compare to.
* @param size Size of the string to compare to.
* @return True if the two words are equivalent.
*/
bool equal(Word, const char *, unsigned) const noexcept;
bool equal(Word word, const char *str, unsigned size) const noexcept;
/**
* Checks if two words in this dictionary's word are equivalent.
* Checks if two words stored in this dictionary are equivalent.
* @param word1 First word to compare
* @param word2 Second word to compare
* @return True if the words are equivalent.
*/
bool equal(Word, Word) const noexcept;
bool equal(Word word1, Word word2) const noexcept;
// Used for case-insensitive comparison between two iterators.
/**
* Generic equality comparison using our own case-insensitive comparator.
* Arguments and return value identical to std::equal.
*/
template<typename Iter1, typename Iter2>
static bool equal(Iter1 b1, Iter1 e1, Iter2 b2) {
LIBALEE_SECTION
constexpr static bool equal(Iter1 b1, Iter1 e1, Iter2 b2) {
return std::equal(b1, e1, b2, eqchars);
}
virtual ~Dictionary() = default;
virtual ~Dictionary() {};
private:
// Case-insensitive comparison.
static bool eqchars(char c1, char c2);
/**
* Case-insensitive character comparison used for dictionary lookup.
* @return True if the characters are equivalent.
*/
LIBALEE_SECTION
constexpr static bool eqchars(char c1, char c2) {
if (isalpha(static_cast<uint8_t>(c1)))
c1 |= 32;
if (isalpha(static_cast<uint8_t>(c2)))
c2 |= 32;
return c1 == c2;
}
};
#endif // ALEEFORTH_DICTIONARY_HPP

@ -16,57 +16,63 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "corewords.hpp"
#include "ctype.hpp"
#include "parser.hpp"
#include "alee.hpp"
Error (*Parser::customParse)(State&, Word) = nullptr;
LIBALEE_SECTION
Error Parser::parse(State& state, const char *str)
{
auto addr = Dictionary::Input;
Cell len = 0;
for (auto ptr = str; *ptr; ++ptr)
++len;
// Set source and input length
const auto len = static_cast<Cell>(strlen(str));
state.dict.write(addr, 0);
state.dict.write(Dictionary::SourceLen, len);
// Fill input buffer with string contents
addr += sizeof(Cell);
while (*str)
state.dict.writebyte(addr++, *str++);
// Zero the remaining input buffer
while (addr < Dictionary::Input + Dictionary::InputCells)
state.dict.writebyte(addr++, '\0');
return parseSource(state);
}
LIBALEE_SECTION
Error Parser::parseSource(State& state)
{
auto word = state.dict.input();
while (word.size() > 0) {
if (auto ret = parseWord(state, word); ret != Error::none)
return ret;
auto err = Error::none;
word = state.dict.input();
}
while (err == Error::none && state.dict.hasInput())
err = parseWord(state, state.dict.input());
return Error::none;
return err;
}
LIBALEE_SECTION
Error Parser::parseWord(State& state, Word word)
{
bool imm;
Addr ins = state.dict.find(word);
Addr ins;
// Search order: dictionary, core word-set, number, custom parse.
ins = state.dict.find(word);
if (ins == 0) {
auto cw = CoreWords::findi(state, word);
if (cw < 0) {
return parseNumber(state, word);
auto r = parseNumber(state, word);
if (r != Error::none)
return customParse ? customParse(state, word) : r;
else
return r;
} else {
ins = cw;
imm = ins == CoreWords::Semicolon;
imm = ins == CoreWords::token(";");
}
} else {
imm = state.dict.read(ins) & Dictionary::Immediate;
@ -81,19 +87,21 @@ Error Parser::parseWord(State& state, Word word)
return Error::none;
}
LIBALEE_SECTION
Error Parser::parseNumber(State& state, Word word)
{
const auto base = state.dict.read(Dictionary::Base);
DoubleCell result = 0;
auto i = word.start;
bool inv;
char c;
auto it = word.begin(&state.dict);
c = state.dict.readbyte(i);
if (inv = c == '-'; inv)
c = state.dict.readbyte(++i);
bool inv = *it == '-';
if (inv)
++it;
const auto end = word.end(&state.dict);
for (uint8_t c; it != end; ++it) {
c = *it;
do {
if (isdigit(c)) {
result *= base;
result += c - '0';
@ -103,29 +111,33 @@ Error Parser::parseNumber(State& state, Word word)
} else {
return Error::noword;
}
if (++i < word.wend)
c = state.dict.readbyte(i);
} while (i < word.wend);
}
if (inv)
result *= -1;
auto value = static_cast<Cell>(result);
processLiteral(state, static_cast<Cell>(result));
return Error::none;
}
LIBALEE_SECTION
void Parser::processLiteral(State& state, Cell value)
{
if (state.compiling()) {
auto ins = CoreWords::findi("_lit");
constexpr auto ins = CoreWords::token("_lit");
// Literal compression: opcodes between WordCount and Begin are unused,
// so we assign literals to them to save space. Opcode "WordCount"
// pushes zero to the stack, "WordCount + 1" pushes a one, etc.
const Cell maxlit = Dictionary::Begin - CoreWords::WordCount;
if (value >= 0 && value < maxlit) {
state.dict.add(value + CoreWords::WordCount);
} else {
if (value >= 0 && value < maxlit)
value += CoreWords::WordCount;
else
state.dict.add(ins);
state.dict.add(value);
}
state.dict.add(value);
} else {
state.push(value);
}
return Error::none;
}

@ -1,55 +1,86 @@
/**
* Alee Forth: A portable and concise Forth implementation in modern C++.
* Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//
/// @file parser.hpp
/// @brief Provides functions to parse text for interpretation/execution.
//
// Alee Forth: A portable and concise Forth implementation in modern C++.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef ALEEFORTH_PARSER_HPP
#define ALEEFORTH_PARSER_HPP
#include "config.hpp"
#include "types.hpp"
#include "state.hpp"
#include <string_view>
/**
* @class Parser
* @brief Provides routines for parsing Forth code.
*/
class Parser
{
public:
/**
* Parses (and evaluates) the given string using the given state.
* The string is stored in the state's input buffer, then parseSource()
* works through that using parseWord(). parseWord() will compile or
* execute as necessary.
* Pointer to a user-provided function that
*/
static Error (*customParse)(State&, Word);
/**
* Parses and evaluates the given string using the given state.
* The string is stored in the state's input buffer before parseSource()
* is called.
* @param state The state to parse and evaluate with.
* @param str The string to parse.
* @return Error token to indicate if parsing was successful.
* @see parseSource(State&)
*/
static Error parse(State& state, const char *str);
/**
* Parses through and compiles or evaluates the words stored in the state's
* input source.
* @param state The state to parse with.
* @return Error token to indicate if parsing was successful.
* @see parseWord(State&, Word)
*/
static Error parse(State&, const char *);
static Error parseSource(State& state);
/**
* Parses (and evaluates) through the words stored in the state's input
* buffer.
* Parses the given value and either pushes it to the stack or compiles
* that functionality.
* @param state The state to give the value to.
* @param value The value to process.
*/
static Error parseSource(State&);
static void processLiteral(State& state, Cell value);
private:
/**
* Parses the given word using the given state.
* @return Error token to indicate if parsing was successful.
*/
static Error parseWord(State&, Word);
/**
* Attempts to parse the given word into a number.
* @param state The state object with the dictionary containing the word.
* @param word The dictionary-stored word (number) to parse.
* @return Error token to indicate if parsing was successful.
*/
static Error parseNumber(State&, Word);
static Error parseNumber(State& state, Word word);
};
#endif // ALEEFORTH_PARSER_HPP

@ -16,50 +16,49 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "corewords.hpp"
#include "state.hpp"
#include "alee.hpp"
#include <cstring>
#include <iterator>
LIBALEE_SECTION
bool State::compiling() const
{
return dict.read(Dictionary::Compiling);
}
LIBALEE_SECTION
void State::compiling(bool yes)
{
dict.write(Dictionary::Compiling, yes);
}
std::pair<Addr, std::jmp_buf> State::save()
LIBALEE_SECTION
State::Context State::save()
{
std::pair<Addr, std::jmp_buf> st;
st.first = ip;
std::memcpy(st.second, jmpbuf, sizeof(std::jmp_buf));
return st;
return context;
}
void State::load(const std::pair<Addr, std::jmp_buf>& st)
LIBALEE_SECTION
void State::load(const State::Context& ctx)
{
ip = st.first;
std::memcpy(jmpbuf, st.second, sizeof(std::jmp_buf));
context = ctx;
}
LIBALEE_SECTION
Error State::execute(Addr addr)
{
auto stat = static_cast<Error>(setjmp(jmpbuf));
auto stat = static_cast<Error>(setjmp(context.jmpbuf));
if (stat == Error::none) {
CoreWords::run(addr, *this);
if (ip >= Dictionary::Begin) {
if (context.ip >= Dictionary::Begin) {
// longjmp will exit this loop.
for (;;)
CoreWords::run(dict.read(ip), *this);
CoreWords::run(dict.read(context.ip), *this);
} else {
// addr was a CoreWord, all done now.
ip = 0;
context.ip = 0;
}
} else if (stat == Error::exit) {
stat = Error::none;
@ -68,6 +67,7 @@ Error State::execute(Addr addr)
return stat;
}
LIBALEE_SECTION
void State::reset()
{
while (size())
@ -76,14 +76,16 @@ void State::reset()
popr();
dict.write(Dictionary::Compiling, 0);
ip = 0;
context.ip = 0;
}
LIBALEE_SECTION
std::size_t State::size() const noexcept
{
return dsp - dstack;
}
LIBALEE_SECTION
std::size_t State::rsize() const noexcept
{
return rsp - rstack;

@ -1,66 +1,80 @@
/**
* Alee Forth: A portable and concise Forth implementation in modern C++.
* Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//
/// @file state.hpp
/// @brief Provides object to manage execution and interpretation state.
//
// Alee Forth: A portable and concise Forth implementation in modern C++.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef ALEEFORTH_STATE_HPP
#define ALEEFORTH_STATE_HPP
#include "config.hpp"
#include "dictionary.hpp"
#include "types.hpp"
#include <csetjmp>
#include <cstddef>
#include <tuple>
constexpr unsigned DataStackSize = 16;
constexpr unsigned ReturnStackSize = 16;
/**
* Size of the primary data stack, number of cells.
*/
constexpr unsigned DataStackSize = 64;
/**
* Size of the return stack, number of cells.
*/
constexpr unsigned ReturnStackSize = 64;
/**
* @class State
* Object to track execution state.
*/
class State
{
public:
Addr ip = 0;
Dictionary& dict;
void (*input)(State&); // User-provided function to collect "stdin" input.
std::jmp_buf jmpbuf = {}; // Used when catching execution errors.
constexpr State(Dictionary& d, void (*i)(State&)):
dict(d), input(i) {}
/** Input functions should add input to the input buffer when available. */
using InputFunc = void (*)(State&);
bool compiling() const;
void compiling(bool);
/** Context object that defines a state of execution. */
struct Context {
Addr ip = 0; /** Instruction pointer */
std::jmp_buf jmpbuf = {}; /** setjmp() buffer for exiting execute() */
};
/**
* Saves execution state so that a new execution can begin.
* Used for EVALUATE.
*/
std::pair<Addr, std::jmp_buf> save();
public:
/** Reference to dictionary used by this state. */
Dictionary& dict;
/**
* Reloads the given execution state.
* Constructs a state object that uses the given dictionary and input
* function.
* @param d The dictionary to be used by this state
* @param i The input collection function to be used by this state
*/
void load(const std::pair<Addr, std::jmp_buf>&);
constexpr State(Dictionary& d, InputFunc i):
dict(d), inputfunc(i), context() {}
/**
* Begins execution at the given execution token.
* Begins execution starting from the given execution token.
* If the token is a CoreWord, this function exits after its execution.
* Otherwise, execution continues until the word's execution completes.
* Encountering an error will cause this function to exit immediately.
* @param addr The token to be executed
* @return An error token to indicate if execution was successful
*/
Error execute(Addr);
Error execute(Addr addr);
/**
* Clears the data and return stacks, sets ip to zero, and clears the
@ -68,56 +82,123 @@ public:
*/
void reset();
/** Returns a reference to the instruction pointer. */
LIBALEE_SECTION
Addr& ip() noexcept {
return context.ip;
}
/** Calls the user input function with this state as the argument. */
LIBALEE_SECTION
void input() noexcept {
inputfunc(*this);
}
/** Returns true if currently in a compiling state. */
bool compiling() const;
/** Sets the compiling state. True if compiling, false if interpreting. */
void compiling(bool);
/** Returns the number of values stored on the data stack. */
std::size_t size() const noexcept;
/** Returns the number of values stored on the return stack. */
std::size_t rsize() const noexcept;
/**
* Returns the current execution state. Usually followed by a load() call.
*/
Context save();
/**
* Reloads the given execution state.
*/
void load(const Context&);
/**
* Pushes the given value to the data stack.
*/
LIBALEE_SECTION
inline void push(Cell value) {
if (dsp == dstack + DataStackSize)
std::longjmp(jmpbuf, static_cast<int>(Error::push));
verify(dsp < dstack + DataStackSize, Error::push);
*dsp++ = value;
}
/**
* Pops a value from the data stack and returns that value.
*/
LIBALEE_SECTION
inline Cell pop() {
if (dsp == dstack)
std::longjmp(jmpbuf, static_cast<int>(Error::pop));
verify(dsp > dstack, Error::pop);
return *--dsp;
}
/**
* Pushes the given value to the return stack.
*/
LIBALEE_SECTION
inline void pushr(Cell value) {
if (rsp == rstack + ReturnStackSize)
std::longjmp(jmpbuf, static_cast<int>(Error::pushr));
verify(rsp < rstack + ReturnStackSize, Error::pushr);
*rsp++ = value;
}
/**
* Pops a value from the return stack and returns that value.
*/
LIBALEE_SECTION
inline Cell popr() {
if (rsp == rstack)
std::longjmp(jmpbuf, static_cast<int>(Error::popr));
verify(rsp > rstack, Error::popr);
return *--rsp;
}
/**
* Returns the value stored at the current data stack position.
*/
LIBALEE_SECTION
inline Cell& top() {
if (dsp == dstack)
std::longjmp(jmpbuf, static_cast<int>(Error::top));
verify(dsp > dstack, Error::top);
return *(dsp - 1);
}
/**
* Picks a value currently stored on the data stack.
* @param i Index from current position to fetch from
* @return The value stored at the given index
*/
LIBALEE_SECTION
inline Cell& pick(std::size_t i) {
if (dsp - i == dstack)
std::longjmp(jmpbuf, static_cast<int>(Error::pick));
verify(dsp - i > dstack, Error::pick);
return *(dsp - i - 1);
}
// Advances the instruction pointer and returns that cell's contents.
/**
* Advances the instruction pointer and returns that cell's contents.
*/
LIBALEE_SECTION
inline Cell beyondip() {
ip += sizeof(Cell);
return dict.read(ip);
context.ip += sizeof(Cell);
return dict.read(context.ip);
}
/**
* Asserts the given condition is true, longjmp-ing if false.
* Used as an exception handler and the method of exiting execution.
* @param condition Condition to be tested
* @param error Error code to report via longjmp() on false condition
*/
LIBALEE_SECTION
inline void verify(bool condition, Error error) {
if (!condition)
std::longjmp(context.jmpbuf, static_cast<int>(error));
}
private:
Cell dstack[DataStackSize] = {};
Cell rstack[ReturnStackSize] = {};
Cell *dsp = dstack;
Cell *rsp = rstack;
InputFunc inputfunc; /** User-provided function to collect user input. */
Context context; /** State's current execution context. */
Cell dstack[DataStackSize] = {}; /** Data stack */
Cell rstack[ReturnStackSize] = {}; /** Return stack */
Cell *dsp = dstack; /** Current data stack position */
Cell *rsp = rstack; /** Current return stack position */
};
#endif // ALEEFORTH_STATE_HPP

@ -16,36 +16,50 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "dictionary.hpp"
#include "types.hpp"
#include "alee.hpp"
LIBALEE_SECTION
Addr Word::size() const noexcept
{
return wend - start;
}
LIBALEE_SECTION
Word::iterator Word::begin(const Dictionary *dict)
{
return iterator(start, dict);
}
LIBALEE_SECTION
Word::iterator Word::end(const Dictionary *dict)
{
return iterator(wend, dict);
}
LIBALEE_SECTION
Word::iterator& Word::iterator::operator++()
{
addr++;
return *this;
}
LIBALEE_SECTION
Word::iterator Word::iterator::operator++(int)
{
const auto copy = *this;
addr++;
return copy;
}
LIBALEE_SECTION
Word::iterator::value_type Word::iterator::operator*()
{
return dict->readbyte(addr);
}
LIBALEE_SECTION
bool Word::iterator::operator!=(const iterator& other)
{
return dict != other.dict || addr != other.addr;
}

@ -1,20 +1,22 @@
/**
* Alee Forth: A portable and concise Forth implementation in modern C++.
* Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//
/// @file types.hpp
/// @brief Defines common types used throughout Alee Forth.
//
// Alee Forth: A portable and concise Forth implementation in modern C++.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef ALEEFORTH_TYPES_HPP
#define ALEEFORTH_TYPES_HPP
@ -22,53 +24,86 @@
#include <cstdint>
#include <iterator>
/**
* Configure the below three types to match your platform.
*/
/** Dictionary address type. Must be equivalent to "unsigned Cell". */
using Addr = uint16_t;
/** Data cell type. Dictionary is basically an array of this type. */
using Cell = int16_t;
/** Double-width cell type. Must be twice the size of Cell. */
using DoubleCell = int32_t;
using DoubleAddr = uint32_t; // Only used for um/mod.
/** Double-width addr type. Must be twice the size of Addr. Used by um/mod. */
using DoubleAddr = uint32_t;
struct Dictionary;
struct State;
using Func = void (*)(State&);
/**
* Error enum to report success or failure of parsing or execution.
*/
enum class Error : int {
none = 0,
push,
pop,
pushr,
popr,
top,
pick,
exit,
noword
none = 0, /** No error */
push, /** Could not push (data stack overflow) */
pop, /** Could not pop (data stack underflow) */
pushr, /** Could not push (return stack overflow) */
popr, /** Could not pop (return stack underflow) */
top, /** Could not fetch data stack top (data stack underflow) */
pick, /** Could not pick data stack value (data stack underflow) */
exit, /** No error, exited from State::execute() */
noword /** Parsing failed because the word was not found */
};
/**
* Stores the start and (past-the-)end addresses of a dictionary's word.
* @class Word
* Stores the beginning and end indices of a dictionary-defined word.
*/
struct Word
class Word
{
/** Beginning (inclusive) index */
Addr start;
/** End (exclusive) index */
Addr wend;
public:
struct iterator;
/**
* Constructs a word with the given start and end indices.
* @param s Start/beginning index
* @param e (past-the-)end index
*/
constexpr explicit Word(Addr s = 0, Addr e = 0):
start(s), wend(e) {}
/**
* Constructs a word using beginning index and length.
* @param s Beginning/start index of word
* @param l Count of bytes until end of word
* @return Resulting Word object
*/
LIBALEE_SECTION
static constexpr Word fromLength(Addr s, Addr l) {
return Word(s, s + l);
}
/** Returns the size of this word in bytes. */
Addr size() const noexcept;
// Iterators provided for std::equal.
struct iterator;
iterator begin(const Dictionary *);
iterator end(const Dictionary *);
/**
* Creates a beginning iterator for the word.
* @param dict Pointer to dictionary object containing this word
* @return Iterator pointing to this word's beginning
*/
iterator begin(const Dictionary *dict);
/**
* Creates an end iterator for the word.
* @param dict Pointer to dictionary object containing this word
* @return Iterator pointing to past-the-end of this word
*/
iterator end(const Dictionary *dict);
/**
* Forward-iterator for iterating through the letters of this word.
*/
struct iterator {
using iterator_category = std::input_iterator_tag;
using value_type = uint8_t;
@ -76,14 +111,26 @@ struct Word
using reference = void;
using difference_type = Cell;
/** Iterator's current address within its containing dictionary. */
Addr addr;
/** Pointer to dictionary that contains this word. */
const Dictionary *dict;
/**
* Constructs a word iterator.
* @param a The address the iterator points to
* @param d The dictionary that contains this word
*/
constexpr iterator(Addr a, const Dictionary *d):
addr(a), dict(d) {}
/** Prefix increment */
iterator& operator++();
/** Postfix increment */
iterator operator++(int);
/** Returns value pointed to by iterator */
value_type operator*();
/** Iterator comparison function (case-insensitive) */
bool operator!=(const iterator&);
};
};

@ -1,52 +1,67 @@
/**
* Alee Forth: A portable and concise Forth implementation in modern C++.
* Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//
/// @file memdict.hpp
/// @brief Simple dictionary implementation using a large memory block.
//
// Alee Forth: A portable and concise Forth implementation in modern C++.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef ALEEFORTH_MEMDICT_HPP
#define ALEEFORTH_MEMDICT_HPP
#include "alee.hpp"
#include "libalee/alee.hpp"
#ifndef MEMDICTSIZE
/** Default dictionary size in bytes. */
#define MEMDICTSIZE (65536)
#endif
/** Size in bytes of a MemDict. */
constexpr unsigned long int MemDictSize = MEMDICTSIZE;
/**
* @class MemDict
* Dictionary implementation that uses a large block of memory.
*/
class MemDict : public Dictionary
{
/** Block of memory to contain dictionary's contents. */
uint8_t dict[MemDictSize] = {0};
public:
/** Returns the value of the cell at the given address. */
virtual Cell read(Addr addr) const noexcept final {
return *reinterpret_cast<const Cell *>(dict + addr);
}
/** Writes the given value to the cell at the given address. */
virtual void write(Addr addr, Cell value) noexcept final {
*reinterpret_cast<Cell *>(dict + addr) = value;
}
/** Returns the value of the byte at the given address. */
virtual uint8_t readbyte(Addr addr) const noexcept final {
return dict[addr];
}
/** Writes the given value to the byte at the given address. */
virtual void writebyte(Addr addr, uint8_t value) noexcept final {
dict[addr] = value;
}
/** Returns the size of the dictionary's memory block. */
virtual unsigned long int capacity() const noexcept final {
return sizeof(dict);
}

@ -0,0 +1,20 @@
# msp430 implementation
This is the MSP430 port of Alee Forth. It produces a binary that enters a REPL made available on the UART peripheral at 115200 baud. The specific target is MSP430G2553.
## Building
1. `make clean` (just in case)
2. `make msp430-prep`: Builds `alee` for the host computer and uses it to create an `alee.dat` blob containing bytecode for `forth/core.fth` and `forth/msp430.fth`.
3. `make msp430`: Produces `alee-msp430`, a standalone binary for the MSP430 with built-in core and msp430 word-sets.
The final binary is < 11 kB and provides 150 bytes for user dictionary in RAM (assuming 512 bytes of total RAM).
## msp430.fth
The msp430 word-set makes programming for the MSP430 easier:
* All register names are defined (P1OUT, ADC10MEM, etc.).
* `r!` and `r@` to write and read device memory (i.e. registers).
* `rset`, `rclr`, `rtgl` work like `r!` but set, clear, or toggle the given value/mask instead.

@ -16,49 +16,73 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "alee.hpp"
#include "libalee/ctype.hpp"
#include "splitmemdict.hpp"
#include "libalee/alee.hpp"
#include "lzss.h"
static const
#include "msp430fr2476_all.h"
#include <msp430.h>
#include "core.fth.h"
#include "splitmemdictrw.hpp"
static char strbuf[32];
static char strbuf[80];
static void readchar(State& state);
static void serput(int c);
static void serputs(const char *s);
static void printint(DoubleCell n, char *buf);
static void printint(DoubleCell n, char *buf, int base);
static Error findword(State&, Word);
static void initGPIO();
static void initClock();
static void initUART();
static void Software_Trim();
#define MCLK_FREQ_MHZ (16)
static void alee_main();
#define ALEE_RODICTSIZE (9088)
__attribute__((section(".lodict")))
#include "core.fth.h"
static bool exitLpm;
static Addr isr_list[24] = {};
using DictType = SplitMemDictRW<ALEE_RODICTSIZE, 32767>;
extern char __dict[sizeof(DictType)];
static auto& dict = *(new (__dict) DictType (alee_dat, 0x10000));
int main()
{
WDTCTL = WDTPW | WDTHOLD;
DCOCTL = 0;
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;
P1SEL |= BIT1 | BIT2;
P1SEL2 |= BIT1 | BIT2;
extern char __libaleebegin;
extern char __libaleeend;
extern char __libaleedst;
std::copy(&__libaleebegin, &__libaleeend, &__libaleedst);
UCA0CTL1 = UCSWRST;
UCA0CTL1 |= UCSSEL_2;
UCA0BR0 = 104;
UCA0BR1 = 0;
UCA0MCTL = UCBRS0;
UCA0CTL1 &= (uint8_t)~UCSWRST;
initGPIO();
initClock();
initUART();
SYSCFG0 = FRWPPW;
__enable_interrupt();
alee_main();
}
static SplitMemDict<alee_dat_len> dict (alee_dat);
LIBALEE_SECTION
void alee_main()
{
(void)alee_dat_len;
State state (dict, readchar);
Parser::customParse = findword;
serputs("alee forth\n\r");
auto ptr = strbuf;
while (1) {
if (IFG2 & UCA0RXIFG) {
char c = UCA0RXBUF;
if (UCA0IFG & UCRXIFG) {
auto c = static_cast<char>(UCA0RXBUF);
serput(c);
if (c == '\r') {
@ -94,32 +118,38 @@ int main()
}
}
static void readchar(State& state)
LIBALEE_SECTION
void readchar(State& state)
{
auto idx = state.dict.read(Dictionary::Input);
Addr addr = Dictionary::Input + sizeof(Cell) + idx;
while (!(IFG2 & UCA0RXIFG));
auto c = UCA0RXBUF;
while (!(UCA0IFG & UCRXIFG));
auto c = static_cast<uint8_t>(UCA0RXBUF);
if (isupper(c))
c += 32;
state.dict.writebyte(addr, c ? c : ' ');
}
LIBALEE_SECTION
void serput(int c)
{
while (!(IFG2 & UCA0TXIFG));
UCA0TXBUF = (char)c;
while (!(UCA0IFG & UCTXIFG));
UCA0TXBUF = static_cast<char>(c);
}
LIBALEE_SECTION
void serputs(const char *s)
{
while (*s)
serput(*s++);
}
void printint(DoubleCell n, char *buf)
LIBALEE_SECTION
void printint(DoubleCell n, char *buf, int base)
{
static const char digit[] = "0123456789ABCDEF";
char *ptr = buf;
bool neg = n < 0;
@ -127,8 +157,8 @@ void printint(DoubleCell n, char *buf)
n = -n;
do {
*ptr++ = (char)(n % 10) + '0';
} while ((n /= 10));
*ptr++ = digit[n % base];
} while ((n /= base));
if (neg)
serput('-');
@ -139,30 +169,285 @@ void printint(DoubleCell n, char *buf)
serput(' ');
}
LIBALEE_SECTION
void user_sys(State& state)
{
switch (state.pop()) {
case 0:
printint(state.pop(), strbuf);
case 0: // .
printint(state.pop(), strbuf, state.dict.read(Dictionary::Base));
break;
case 1:
printint(static_cast<Addr>(state.pop()), strbuf);
case 1: // unused
state.push(static_cast<Addr>(state.dict.capacity() - state.dict.here()));
break;
case 2:
case 2: // emit
serput(state.pop());
break;
case 3:
case 10:
{ auto index = state.pop() - 20;
isr_list[index] = state.pop(); }
break;
case 11:
{ auto addr = state.pop();
*reinterpret_cast<uint8_t *>(addr) = state.pop() & 0xFFu; }
break;
case 4:
case 12:
state.push(*reinterpret_cast<uint8_t *>(state.pop()));
break;
case 13:
{ auto addr = state.pop();
*reinterpret_cast<uint16_t *>(addr) = state.pop() & 0xFFFFu; }
break;
case 14:
state.push(*reinterpret_cast<uint16_t *>(state.pop()));
break;
case 15:
_bis_SR_register(state.pop());
break;
case 16:
_bic_SR_register(state.pop());
break;
case 17:
exitLpm |= true;
break;
case 50:
Parser::customParse = nullptr;
extern char _etext;
state.push((Addr)&_etext);
break;
default:
break;
}
}
extern "C" int atexit(void (*)()) { return 0; }
void operator delete(void *) {}
void operator delete(void *, std::size_t) {}
#define LZSS_MAGIC_SEPARATOR (0xFB)
static uint8_t lzword[32];
static int lzwlen;
static uint8_t lzbuf[32];
static uint8_t *lzptr;
Error findword(State& state, Word word)
{
uint8_t *ptr = lzword;
for (auto it = word.begin(&state.dict); it != word.end(&state.dict); ++it) {
*ptr = *it;
if (islower(*ptr))
*ptr -= 32;
++ptr;
}
lzwlen = (int)(ptr - lzword);
lzptr = lzbuf;
lzssinit(msp430fr2476_all_lzss, msp430fr2476_all_lzss_len);
auto ret = decode([](int c) {
if (c != LZSS_MAGIC_SEPARATOR) {
*lzptr++ = (uint8_t)c;
} else {
if (lzwlen == lzptr - lzbuf - 2 && std::equal(lzbuf, lzptr - 2, lzword)) {
lzwlen = (*(lzptr - 2) << 8) | *(lzptr - 1);
return 1;
} else {
lzptr = lzbuf;
}
}
return 0;
});
if (ret == EOF) {
return Error::noword;
} else {
Parser::processLiteral(state, (Cell)lzwlen);
return Error::none;
}
}
void initGPIO()
{
// Unnecessary, but done by TI example
P1DIR = 0xFF; P2DIR = 0xFF;
P1REN = 0xFF; P2REN = 0xFF;
P1OUT = 0x00; P2OUT = 0x00;
// Set LED pins to outputs
P6DIR |= BIT0 | BIT1 | BIT2;
P6OUT |= BIT0 | BIT1 | BIT2;
P5DIR |= BIT5 | BIT6 | BIT7;
P5OUT |= BIT5 | BIT6 | BIT7;
// Setup buttons w/ pullups
P3DIR &= ~BIT4; P3REN |= BIT4; P3OUT |= BIT4;
P2DIR &= ~BIT3; P2REN |= BIT3; P2OUT |= BIT3;
// Allow GPIO configurations to be applied
PM5CTL0 &= ~LOCKLPM5;
// Safety measure, prevent unwarranted interrupts
P5IFG = 0;
P6IFG = 0;
}
void initClock()
{
static_assert(MCLK_FREQ_MHZ == 16);
// Configure one FRAM waitstate as required by the device datasheet for MCLK
// operation beyond 8MHz _before_ configuring the clock system.
FRCTL0 = FRCTLPW | NWAITS_1;
P2SEL0 |= BIT0 | BIT1; // P2.0~P2.1: crystal pins
do
{
CSCTL7 &= ~(XT1OFFG | DCOFFG); // Clear XT1 and DCO fault flag
SFRIFG1 &= ~OFIFG;
} while (SFRIFG1 & OFIFG); // Test oscillator fault flag
__bis_SR_register(SCG0); // disable FLL
CSCTL3 |= SELREF__XT1CLK; // Set XT1 as FLL reference source
CSCTL1 = DCOFTRIMEN_1 | DCOFTRIM0 | DCOFTRIM1 | DCORSEL_5;// DCOFTRIM=5, DCO Range = 16MHz
CSCTL2 = FLLD_0 + 487; // DCOCLKDIV = 16MHz
__delay_cycles(3);
__bic_SR_register(SCG0); // enable FLL
Software_Trim(); // Software Trim to get the best DCOFTRIM value
CSCTL4 = SELMS__DCOCLKDIV | SELA__XT1CLK; // set XT1 (~32768Hz) as ACLK source, ACLK = 32768Hz
// default DCOCLKDIV as MCLK and SMCLK source
}
void initUART()
{
// Configure UART pins
P5SEL0 |= BIT1 | BIT2;
SYSCFG3 |= USCIA0RMP; // Set the remapping source
UCA0CTLW0 |= UCSWRST;
UCA0CTLW0 |= UCSSEL__SMCLK; // 16 MHz
// Baud Rate calculation
// N = 16MHz / 115200 = 138.888
// OS16 = 1, UCBRx = INT(N/16) = 8(.6806)
// UCBRFx = INT( ((N/16) - UCBRx) * 16) = 10(.8896)
UCA0BRW = 8;
UCA0MCTLW = 0xD600 | 0x00A0 | UCOS16;
UCA0CTLW0 &= ~UCSWRST; // Initialize eUSCI
}
void Software_Trim()
{
unsigned int oldDcoTap = 0xffff;
unsigned int newDcoTap = 0xffff;
unsigned int newDcoDelta = 0xffff;
unsigned int bestDcoDelta = 0xffff;
unsigned int csCtl0Copy = 0;
unsigned int csCtl1Copy = 0;
unsigned int csCtl0Read = 0;
unsigned int csCtl1Read = 0;
unsigned int dcoFreqTrim = 3;
unsigned char endLoop = 0;
do
{
CSCTL0 = 0x100; // DCO Tap = 256
do
{
CSCTL7 &= ~DCOFFG; // Clear DCO fault flag
}while (CSCTL7 & DCOFFG); // Test DCO fault flag
__delay_cycles((unsigned int)3000 * MCLK_FREQ_MHZ);// Wait FLL lock status (FLLUNLOCK) to be stable
// Suggest to wait 24 cycles of divided FLL reference clock
while((CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)) && ((CSCTL7 & DCOFFG) == 0));
csCtl0Read = CSCTL0; // Read CSCTL0
csCtl1Read = CSCTL1; // Read CSCTL1
oldDcoTap = newDcoTap; // Record DCOTAP value of last time
newDcoTap = csCtl0Read & 0x01ff; // Get DCOTAP value of this time
dcoFreqTrim = (csCtl1Read & 0x0070)>>4;// Get DCOFTRIM value
if(newDcoTap < 256) // DCOTAP < 256
{
newDcoDelta = 256 - newDcoTap; // Delta value between DCPTAP and 256
if((oldDcoTap != 0xffff) && (oldDcoTap >= 256)) // DCOTAP cross 256
endLoop = 1; // Stop while loop
else
{
dcoFreqTrim--;
CSCTL1 = (csCtl1Read & (~DCOFTRIM)) | (dcoFreqTrim<<4);
}
}
else // DCOTAP >= 256
{
newDcoDelta = newDcoTap - 256; // Delta value between DCPTAP and 256
if(oldDcoTap < 256) // DCOTAP cross 256
endLoop = 1; // Stop while loop
else
{
dcoFreqTrim++;
CSCTL1 = (csCtl1Read & (~DCOFTRIM)) | (dcoFreqTrim<<4);
}
}
if(newDcoDelta < bestDcoDelta) // Record DCOTAP closest to 256
{
csCtl0Copy = csCtl0Read;
csCtl1Copy = csCtl1Read;
bestDcoDelta = newDcoDelta;
}
}while(endLoop == 0); // Poll until endLoop == 1
CSCTL0 = csCtl0Copy; // Reload locked DCOTAP
CSCTL1 = csCtl1Copy; // Reload locked DCOFTRIM
while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // Poll until FLL is locked
}
bool alee_isr_handle(unsigned index)
{
const Addr isr = isr_list[index];
if (isr != 0) {
State isrstate (dict, readchar);
exitLpm = false;
isrstate.execute(isr);
return exitLpm;
}
return false;
}
#define DEFINE_ISR(VVV, III) \
__attribute__((interrupt(VVV))) \
void VVV##_ISR() { \
if (alee_isr_handle(III)) \
_low_power_mode_off_on_exit(); }
DEFINE_ISR(ECOMP0_VECTOR, 0)
DEFINE_ISR(PORT6_VECTOR, 1)
DEFINE_ISR(PORT5_VECTOR, 2)
DEFINE_ISR(PORT4_VECTOR, 3)
DEFINE_ISR(PORT3_VECTOR, 4)
DEFINE_ISR(PORT2_VECTOR, 5)
DEFINE_ISR(PORT1_VECTOR, 6)
DEFINE_ISR(ADC_VECTOR, 7)
DEFINE_ISR(EUSCI_B1_VECTOR, 8)
DEFINE_ISR(EUSCI_B0_VECTOR, 9)
DEFINE_ISR(EUSCI_A1_VECTOR, 10)
DEFINE_ISR(EUSCI_A0_VECTOR, 11)
DEFINE_ISR(WDT_VECTOR, 12)
DEFINE_ISR(RTC_VECTOR, 13)
DEFINE_ISR(TIMER0_B1_VECTOR, 14)
DEFINE_ISR(TIMER0_B0_VECTOR, 15)
DEFINE_ISR(TIMER3_A1_VECTOR, 16)
DEFINE_ISR(TIMER3_A0_VECTOR, 17)
DEFINE_ISR(TIMER2_A1_VECTOR, 18)
DEFINE_ISR(TIMER2_A0_VECTOR, 19)
DEFINE_ISR(TIMER1_A1_VECTOR, 20)
DEFINE_ISR(TIMER1_A0_VECTOR, 21)
DEFINE_ISR(TIMER0_A1_VECTOR, 22)
DEFINE_ISR(TIMER0_A0_VECTOR, 23)
// Override newlib's free to save hundreds of bytes
extern "C" void free(void *) {}

@ -0,0 +1,9 @@
#!/bin/bash -x
cat msp430fr2476_symbols.ld | grep "^PROVIDE.*" | sed -e "s/^PROVIDE(//" -e "s/[ ][ ]*//" -e "s/=.0x/\\\\x/" -e "s/..);/\\\\x&/" -e "s/);//" > msp430fr2476_symbols.dat
grep -E "^#define \w+\s+\([0-9].*$" msp430fr2476.h | sed -e "s/).*$/)/" -e "s/^#define //" -e "s/[ ][ ]*//" -e "s/(0x/\\\\x/" -e "s/..)/\\\\x&/" -e "s/)//" -e "s/(/\\\\x/" -e "s/\\\\x\\\\/\\\\x00\\\\/" > msp430fr2476.dat
cat msp430fr2476_symbols.dat msp430fr2476.dat | tr '\n' '\373' | tr -d '\r' > msp430fr2476_all.dat
echo -e "$(cat msp430fr2476_all.dat)" > msp430fr2476_all.bin
./lzss e msp430fr2476_all.bin msp430fr2476_all.lzss
ls -l msp430fr2476_all.lzss
xxd -i msp430fr2476_all.lzss > msp430fr2476_all.h

@ -0,0 +1,15 @@
unsigned char RXData = 0;
unsigned char TXData;
: spi-init
bit2 bit5 or bit6 or p3sel0 byte set
ucb1ctlw0
ucswrst over reg set
ucmst ucsync or ucckpl or ucmsb or over reg set
ucssel__aclk over reg set
2 ucb1brw reg!
ucswrst swap reg clear ;
: spi-emit
begin ucb1ifg reg@ uctxifg and until
ucb1txbuf reg! ;

@ -0,0 +1,13 @@
\ UART example, 19200 baud, pins D0/1
: uart-init ( -- )
bit5 bit6 or p2sel0 byte set
ucswrst uca1ctlw0 reg set
ucssel__smclk uca1ctlw0 reg set
52 uca1brw reg!
18688 ucos16 or ucbrf0 or uca1mctlw reg!
ucswrst uca1ctlw0 reg clear ;
: uart-emit ( n -- )
begin uca1ifg reg@ uctxifg and until
uca1txbuf byte! ;

@ -0,0 +1,181 @@
/* LZSS encoder-decoder (Haruhiko Okumura; public domain) */
#include <stdio.h>
#include <stdlib.h>
#define EI 8 /* typically 10..13 */
#define EJ 3 /* typically 4..5 */
#define P 1 /* If match length <= P then output one character */
#define N (1 << EI) /* buffer size */
#define F ((1 << EJ) + 1) /* lookahead buffer size */
int bit_buffer = 0, bit_mask = 128;
unsigned long codecount = 0, textcount = 0;
unsigned char buffer[N * 2];
FILE *infile, *outfile;
void error(void)
{
printf("Output error\n"); exit(1);
}
void putbit1(void)
{
bit_buffer |= bit_mask;
if ((bit_mask >>= 1) == 0) {
if (fputc(bit_buffer, outfile) == EOF) error();
bit_buffer = 0; bit_mask = 128; codecount++;
}
}
void putbit0(void)
{
if ((bit_mask >>= 1) == 0) {
if (fputc(bit_buffer, outfile) == EOF) error();
bit_buffer = 0; bit_mask = 128; codecount++;
}
}
void flush_bit_buffer(void)
{
if (bit_mask != 128) {
if (fputc(bit_buffer, outfile) == EOF) error();
codecount++;
}
}
void output1(int c)
{
int mask;
putbit1();
mask = 256;
while (mask >>= 1) {
if (c & mask) putbit1();
else putbit0();
}
}
void output2(int x, int y)
{
int mask;
putbit0();
mask = N;
while (mask >>= 1) {
if (x & mask) putbit1();
else putbit0();
}
mask = (1 << EJ);
while (mask >>= 1) {
if (y & mask) putbit1();
else putbit0();
}
}
void encode(void)
{
int i, j, f1, x, y, r, s, bufferend, c;
for (i = 0; i < N - F; i++) buffer[i] = ' ';
for (i = N - F; i < N * 2; i++) {
if ((c = fgetc(infile)) == EOF) break;
buffer[i] = c; textcount++;
}
bufferend = i; r = N - F; s = 0;
while (r < bufferend) {
f1 = (F <= bufferend - r) ? F : bufferend - r;
x = 0; y = 1; c = buffer[r];
for (i = r - 1; i >= s; i--)
if (buffer[i] == c) {
for (j = 1; j < f1; j++)
if (buffer[i + j] != buffer[r + j]) break;
if (j > y) {
x = i; y = j;
}
}
if (y <= P) { y = 1; output1(c); }
else output2(x & (N - 1), y - 2);
r += y; s += y;
if (r >= N * 2 - F) {
for (i = 0; i < N; i++) buffer[i] = buffer[i + N];
bufferend -= N; r -= N; s -= N;
while (bufferend < N * 2) {
if ((c = fgetc(infile)) == EOF) break;
buffer[bufferend++] = c; textcount++;
}
}
}
flush_bit_buffer();
printf("text: %ld bytes\n", textcount);
printf("code: %ld bytes (%ld%%)\n",
codecount, (codecount * 100) / textcount);
}
int getbit(int n) /* get n bits */
{
int i, x;
static int buf, mask = 0;
x = 0;
for (i = 0; i < n; i++) {
if (mask == 0) {
if ((buf = fgetc(infile)) == EOF) return EOF;
mask = 128;
}
x <<= 1;
if (buf & mask) x++;
mask >>= 1;
}
return x;
}
void decode(void)
{
int i, j, k, r, c;
for (i = 0; i < N - F; i++) buffer[i] = ' ';
r = N - F;
while ((c = getbit(1)) != EOF) {
if (c) {
if ((c = getbit(8)) == EOF) break;
fputc(c, outfile);
buffer[r++] = c; r &= (N - 1);
} else {
if ((i = getbit(EI)) == EOF) break;
if ((j = getbit(EJ)) == EOF) break;
for (k = 0; k <= j + 1; k++) {
c = buffer[(i + k) & (N - 1)];
fputc(c, outfile);
buffer[r++] = c; r &= (N - 1);
}
}
}
}
int main(int argc, char *argv[])
{
int enc;
char *s;
if (argc != 4) {
printf("Usage: lzss e/d infile outfile\n\te = encode\td = decode\n");
return 1;
}
s = argv[1];
if (s[1] == 0 && (*s == 'd' || *s == 'D' || *s == 'e' || *s == 'E'))
enc = (*s == 'e' || *s == 'E');
else {
printf("? %s\n", s); return 1;
}
if ((infile = fopen(argv[2], "rb")) == NULL) {
printf("? %s\n", argv[2]); return 1;
}
if ((outfile = fopen(argv[3], "wb")) == NULL) {
printf("? %s\n", argv[3]); return 1;
}
if (enc) encode(); else decode();
fclose(infile); fclose(outfile);
return 0;
}

@ -0,0 +1,74 @@
/* LZSS encoder-decoder (Haruhiko Okumura; public domain) */
/* Modified by Clyne Sullivan to focus on streamed decompression. */
#ifndef EOF
#define EOF (-1)
#endif
#define EI 8 /* typically 10..13 */
#define EJ 3 /* typically 4..5 */
#define N (1 << EI) /* buffer size */
#define F ((1 << EJ) + 1) /* lookahead buffer size */
static unsigned char buffer[N * 2];
static const unsigned char *inbuffer;
static unsigned int insize, inidx;
static int buf, mask;
/* Prepares decode() to decompress the given data. */
void lzssinit(const unsigned char *inb, unsigned int ins)
{
inbuffer = inb;
insize = ins;
inidx = 0;
buf = 0;
mask = 0;
}
int getbit(int n) /* get n bits */
{
int i, x;
x = 0;
for (i = 0; i < n; i++) {
if (mask == 0) {
if (inidx >= insize)
return EOF;
buf = inbuffer[inidx++];
mask = 128;
}
x <<= 1;
if (buf & mask) x++;
mask >>= 1;
}
return x;
}
/* handleoutput() receives each decompressed byte, return zero if want more. */
int decode(int (*handleoutput)(int))
{
int i, j, k, r, c, ret;
for (i = 0; i < N - F; i++) buffer[i] = ' ';
r = N - F;
while ((c = getbit(1)) != EOF) {
if (c) {
if ((c = getbit(8)) == EOF) break;
if ((ret = handleoutput(c)))
return ret;
buffer[r++] =(unsigned char) c; r &= (N - 1);
} else {
if ((i = getbit(EI)) == EOF) break;
if ((j = getbit(EJ)) == EOF) break;
for (k = 0; k <= j + 1; k++) {
c = buffer[(i + k) & (N - 1)];
if ((ret = handleoutput(c)))
return ret;
buffer[r++] = (unsigned char)c; r &= (N - 1);
}
}
}
return EOF;
}

4301
msp430/msp430fr2476.h vendored

File diff suppressed because it is too large Load Diff

@ -0,0 +1,376 @@
/* ============================================================================ */
/* Copyright (c) 2021, Texas Instruments Incorporated */
/* All rights reserved. */
/* */
/* Redistribution and use in source and binary forms, with or without */
/* modification, are permitted provided that the following conditions */
/* are met: */
/* */
/* * Redistributions of source code must retain the above copyright */
/* notice, this list of conditions and the following disclaimer. */
/* */
/* * Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the */
/* documentation and/or other materials provided with the distribution. */
/* */
/* * Neither the name of Texas Instruments Incorporated nor the names of */
/* its contributors may be used to endorse or promote products derived */
/* from this software without specific prior written permission. */
/* */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" */
/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, */
/* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR */
/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, */
/* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; */
/* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */
/* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR */
/* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
/* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/* ============================================================================ */
/* This file supports MSP430FR2476 devices. */
/* Version: 1.212 */
/* Default linker script, for normal executables */
OUTPUT_ARCH(msp430)
ENTRY(_start)
MEMORY {
TINYRAM : ORIGIN = 0x0006, LENGTH = 0x001A /* END=0x001F, size 26 */
BSL0 : ORIGIN = 0x1000, LENGTH = 0x0800 /* END=0x17FF, size 2048 */
TLVMEM : ORIGIN = 0x1A00, LENGTH = 0x0200 /* END=0x1BFF, size 512 */
BOOTCODE : ORIGIN = 0x1C00, LENGTH = 0x0400 /* END=0x1FFF, size 1024 */
ROMLIB : ORIGIN = 0xC0000, LENGTH = 0x4000 /* END=0xC3FFF, size 16384 */
BSL1 : ORIGIN = 0xFFC00, LENGTH = 0x0400 /* END=0xFFFFF, size 1024 */
RAM (rwx) : ORIGIN = 0x2000, LENGTH = 0x2000 /* END=0x3FFF, size 8192 */
INFOMEM : ORIGIN = 0x1800, LENGTH = 0x0200 /* END=0x19FF, size 512 */
FRAM (rwx) : ORIGIN = 0x8000, LENGTH = 0x7F80 /* END=0xFF7F, size 32640 */
HIFRAM (rxw) : ORIGIN = 0x10000, LENGTH = 0x00007FFF
JTAGSIGNATURE : ORIGIN = 0xFF80, LENGTH = 0x0004
BSLSIGNATURE : ORIGIN = 0xFF84, LENGTH = 0x0004
BSLCONFIGURATIONSIGNATURE : ORIGIN = 0xFF88, LENGTH = 0x0002
BSLCONFIGURATION : ORIGIN = 0xFF8A, LENGTH = 0x0002
BSLI2CADDRESS : ORIGIN = 0xFFA0, LENGTH = 0x0002
VECT0 : ORIGIN = 0xFFA2, LENGTH = 0x0002
VECT1 : ORIGIN = 0xFFA4, LENGTH = 0x0002
VECT2 : ORIGIN = 0xFFA6, LENGTH = 0x0002
VECT3 : ORIGIN = 0xFFA8, LENGTH = 0x0002
VECT4 : ORIGIN = 0xFFAA, LENGTH = 0x0002
VECT5 : ORIGIN = 0xFFAC, LENGTH = 0x0002
VECT6 : ORIGIN = 0xFFAE, LENGTH = 0x0002
VECT7 : ORIGIN = 0xFFB0, LENGTH = 0x0002
VECT8 : ORIGIN = 0xFFB2, LENGTH = 0x0002
VECT9 : ORIGIN = 0xFFB4, LENGTH = 0x0002
VECT10 : ORIGIN = 0xFFB6, LENGTH = 0x0002
VECT11 : ORIGIN = 0xFFB8, LENGTH = 0x0002
VECT12 : ORIGIN = 0xFFBA, LENGTH = 0x0002
VECT13 : ORIGIN = 0xFFBC, LENGTH = 0x0002
VECT14 : ORIGIN = 0xFFBE, LENGTH = 0x0002
VECT15 : ORIGIN = 0xFFC0, LENGTH = 0x0002
VECT16 : ORIGIN = 0xFFC2, LENGTH = 0x0002
VECT17 : ORIGIN = 0xFFC4, LENGTH = 0x0002
VECT18 : ORIGIN = 0xFFC6, LENGTH = 0x0002
VECT19 : ORIGIN = 0xFFC8, LENGTH = 0x0002
VECT20 : ORIGIN = 0xFFCA, LENGTH = 0x0002
VECT21 : ORIGIN = 0xFFCC, LENGTH = 0x0002
VECT22 : ORIGIN = 0xFFCE, LENGTH = 0x0002
VECT23 : ORIGIN = 0xFFD0, LENGTH = 0x0002
VECT24 : ORIGIN = 0xFFD2, LENGTH = 0x0002
VECT25 : ORIGIN = 0xFFD4, LENGTH = 0x0002
VECT26 : ORIGIN = 0xFFD6, LENGTH = 0x0002
VECT27 : ORIGIN = 0xFFD8, LENGTH = 0x0002
VECT28 : ORIGIN = 0xFFDA, LENGTH = 0x0002
VECT29 : ORIGIN = 0xFFDC, LENGTH = 0x0002
VECT30 : ORIGIN = 0xFFDE, LENGTH = 0x0002
VECT31 : ORIGIN = 0xFFE0, LENGTH = 0x0002
VECT32 : ORIGIN = 0xFFE2, LENGTH = 0x0002
VECT33 : ORIGIN = 0xFFE4, LENGTH = 0x0002
VECT34 : ORIGIN = 0xFFE6, LENGTH = 0x0002
VECT35 : ORIGIN = 0xFFE8, LENGTH = 0x0002
VECT36 : ORIGIN = 0xFFEA, LENGTH = 0x0002
VECT37 : ORIGIN = 0xFFEC, LENGTH = 0x0002
VECT38 : ORIGIN = 0xFFEE, LENGTH = 0x0002
VECT39 : ORIGIN = 0xFFF0, LENGTH = 0x0002
VECT40 : ORIGIN = 0xFFF2, LENGTH = 0x0002
VECT41 : ORIGIN = 0xFFF4, LENGTH = 0x0002
VECT42 : ORIGIN = 0xFFF6, LENGTH = 0x0002
VECT43 : ORIGIN = 0xFFF8, LENGTH = 0x0002
VECT44 : ORIGIN = 0xFFFA, LENGTH = 0x0002
VECT45 : ORIGIN = 0xFFFC, LENGTH = 0x0002
RESETVEC : ORIGIN = 0xFFFE, LENGTH = 0x0002
}
SECTIONS
{
.jtagsignature : {} > JTAGSIGNATURE
.bslsignature : {} > BSLSIGNATURE
.bslconfigsignature : {} > BSLCONFIGURATIONSIGNATURE
.bslconfig : {} > BSLCONFIGURATION
.bsli2caddress : {} > BSLI2CADDRESS
__interrupt_vector_0 : { KEEP (*(__interrupt_vector_0 )) } > VECT0
__interrupt_vector_1 : { KEEP (*(__interrupt_vector_1 )) } > VECT1
__interrupt_vector_2 : { KEEP (*(__interrupt_vector_2 )) } > VECT2
__interrupt_vector_3 : { KEEP (*(__interrupt_vector_3 )) } > VECT3
__interrupt_vector_4 : { KEEP (*(__interrupt_vector_4 )) } > VECT4
__interrupt_vector_5 : { KEEP (*(__interrupt_vector_5 )) } > VECT5
__interrupt_vector_6 : { KEEP (*(__interrupt_vector_6 )) } > VECT6
__interrupt_vector_7 : { KEEP (*(__interrupt_vector_7 )) } > VECT7
__interrupt_vector_8 : { KEEP (*(__interrupt_vector_8 )) } > VECT8
__interrupt_vector_9 : { KEEP (*(__interrupt_vector_9 )) } > VECT9
__interrupt_vector_10 : { KEEP (*(__interrupt_vector_10)) } > VECT10
__interrupt_vector_11 : { KEEP (*(__interrupt_vector_11)) } > VECT11
__interrupt_vector_12 : { KEEP (*(__interrupt_vector_12)) } > VECT12
__interrupt_vector_13 : { KEEP (*(__interrupt_vector_13)) } > VECT13
__interrupt_vector_14 : { KEEP (*(__interrupt_vector_14)) } > VECT14
__interrupt_vector_15 : { KEEP (*(__interrupt_vector_15)) } > VECT15
__interrupt_vector_16 : { KEEP (*(__interrupt_vector_16)) } > VECT16
__interrupt_vector_17 : { KEEP (*(__interrupt_vector_17)) } > VECT17
__interrupt_vector_18 : { KEEP (*(__interrupt_vector_18)) } > VECT18
__interrupt_vector_19 : { KEEP (*(__interrupt_vector_19)) } > VECT19
__interrupt_vector_20 : { KEEP (*(__interrupt_vector_20)) KEEP (*(__interrupt_vector_ecomp0)) } > VECT20
__interrupt_vector_21 : { KEEP (*(__interrupt_vector_21)) KEEP (*(__interrupt_vector_port6)) } > VECT21
__interrupt_vector_22 : { KEEP (*(__interrupt_vector_22)) KEEP (*(__interrupt_vector_port5)) } > VECT22
__interrupt_vector_23 : { KEEP (*(__interrupt_vector_23)) KEEP (*(__interrupt_vector_port4)) } > VECT23
__interrupt_vector_24 : { KEEP (*(__interrupt_vector_24)) KEEP (*(__interrupt_vector_port3)) } > VECT24
__interrupt_vector_25 : { KEEP (*(__interrupt_vector_25)) KEEP (*(__interrupt_vector_port2)) } > VECT25
__interrupt_vector_26 : { KEEP (*(__interrupt_vector_26)) KEEP (*(__interrupt_vector_port1)) } > VECT26
__interrupt_vector_27 : { KEEP (*(__interrupt_vector_27)) KEEP (*(__interrupt_vector_adc)) } > VECT27
__interrupt_vector_28 : { KEEP (*(__interrupt_vector_28)) KEEP (*(__interrupt_vector_eusci_b1)) } > VECT28
__interrupt_vector_29 : { KEEP (*(__interrupt_vector_29)) KEEP (*(__interrupt_vector_eusci_b0)) } > VECT29
__interrupt_vector_30 : { KEEP (*(__interrupt_vector_30)) KEEP (*(__interrupt_vector_eusci_a1)) } > VECT30
__interrupt_vector_31 : { KEEP (*(__interrupt_vector_31)) KEEP (*(__interrupt_vector_eusci_a0)) } > VECT31
__interrupt_vector_32 : { KEEP (*(__interrupt_vector_32)) KEEP (*(__interrupt_vector_wdt)) } > VECT32
__interrupt_vector_33 : { KEEP (*(__interrupt_vector_33)) KEEP (*(__interrupt_vector_rtc)) } > VECT33
__interrupt_vector_34 : { KEEP (*(__interrupt_vector_34)) KEEP (*(__interrupt_vector_timer0_b1)) } > VECT34
__interrupt_vector_35 : { KEEP (*(__interrupt_vector_35)) KEEP (*(__interrupt_vector_timer0_b0)) } > VECT35
__interrupt_vector_36 : { KEEP (*(__interrupt_vector_36)) KEEP (*(__interrupt_vector_timer3_a1)) } > VECT36
__interrupt_vector_37 : { KEEP (*(__interrupt_vector_37)) KEEP (*(__interrupt_vector_timer3_a0)) } > VECT37
__interrupt_vector_38 : { KEEP (*(__interrupt_vector_38)) KEEP (*(__interrupt_vector_timer2_a1)) } > VECT38
__interrupt_vector_39 : { KEEP (*(__interrupt_vector_39)) KEEP (*(__interrupt_vector_timer2_a0)) } > VECT39
__interrupt_vector_40 : { KEEP (*(__interrupt_vector_40)) KEEP (*(__interrupt_vector_timer1_a1)) } > VECT40
__interrupt_vector_41 : { KEEP (*(__interrupt_vector_41)) KEEP (*(__interrupt_vector_timer1_a0)) } > VECT41
__interrupt_vector_42 : { KEEP (*(__interrupt_vector_42)) KEEP (*(__interrupt_vector_timer0_a1)) } > VECT42
__interrupt_vector_43 : { KEEP (*(__interrupt_vector_43)) KEEP (*(__interrupt_vector_timer0_a0)) } > VECT43
__interrupt_vector_44 : { KEEP (*(__interrupt_vector_44)) KEEP (*(__interrupt_vector_unmi)) } > VECT44
__interrupt_vector_45 : { KEEP (*(__interrupt_vector_45)) KEEP (*(__interrupt_vector_sysnmi)) } > VECT45
__reset_vector :
{
KEEP (*(__interrupt_vector_46))
KEEP (*(__interrupt_vector_reset))
KEEP (*(.resetvec))
} > RESETVEC
.rodata :
{
. = ALIGN(2);
*(.rodata .rodata.* .gnu.linkonce.r.* .const .const:*)
*(.rodata1)
} > FRAM
/* Note: This is a separate .rodata section for sections which are
read only but which older linkers treat as read-write.
This prevents older linkers from marking the entire .rodata
section as read-write. */
.rodata2 :
{
. = ALIGN(2);
PROVIDE (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE (__preinit_array_end = .);
. = ALIGN(2);
PROVIDE (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE (__init_array_end = .);
. = ALIGN(2);
PROVIDE (__fini_array_start = .);
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
PROVIDE (__fini_array_end = .);
. = ALIGN(2);
/* gcc uses crtbegin.o to find the start of the constructors, so
we make sure it is first. Because this is a wildcard, it
doesn't matter if the user does not actually link against
crtbegin.o; the linker won't look for a file to match a
wildcard. The wildcard also means that it doesn't matter which
directory crtbegin.o is in. */
KEEP (*crtbegin*.o(.ctors))
/* We don't want to include the .ctor section from the crtend.o
file until after the sorted ctors. The .ctor section from
the crtend file contains the end of ctors marker and it must
be last */
KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
KEEP (*crtbegin*.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
} > FRAM
.tinyram : {
PROVIDE (__dict = .);
} > TINYRAM
.libalee : {
. = ALIGN(2);
PROVIDE (__libaleedst = .);
*(.libalee)
} > RAM AT> FRAM
PROVIDE(__libaleebegin = LOADADDR(.libalee));
PROVIDE (__libaleeend = LOADADDR(.libalee) + SIZEOF(.libalee));
.data :
{
. = ALIGN(2);
PROVIDE (__datastart = .);
*(.data .data.* .gnu.linkonce.d.*)
KEEP (*(.gnu.linkonce.d.*personality*))
SORT(CONSTRUCTORS)
*(.data1)
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
. = ALIGN(2);
*(.sdata .sdata.* .gnu.linkonce.s.* D_2 D_1)
. = ALIGN(2);
_edata = .;
PROVIDE (edata = .);
PROVIDE (__dataend = .);
} > RAM AT> FRAM
/* Note that crt0 assumes this is a multiple of two; all the
start/stop symbols are also assumed word-aligned. */
PROVIDE(__romdatastart = LOADADDR(.data));
PROVIDE (__romdatacopysize = SIZEOF(.data));
.bss :
{
. = ALIGN(2);
PROVIDE (__bssstart = .);
*(.sbss .sbss.*)
*(.bss .bss.* .gnu.linkonce.b.*)
. = ALIGN(2);
*(COMMON)
PROVIDE (__bssend = .);
} > RAM
PROVIDE (__bsssize = SIZEOF(.bss));
/* This section contains data that is not initialised during load
or application reset. */
.noinit (NOLOAD) :
{
. = ALIGN(2);
PROVIDE (__noinit_start = .);
*(.noinit)
. = ALIGN(2);
PROVIDE (__noinit_end = .);
} > RAM
.stack (ORIGIN (RAM) + LENGTH(RAM)) :
{
PROVIDE (__stack = .);
*(.stack)
}
.hidict :
{
. = ALIGN(2);
*(.hidict)
} > HIFRAM
.text :
{
PROVIDE (_start = .);
. = ALIGN(2);
KEEP (*(SORT(.crt_*)))
. = ALIGN(2);
*(.text .stub .text.* .gnu.linkonce.t.* .text:*)
. = ALIGN(2);
KEEP (*(.init))
KEEP (*(.fini))
KEEP (*(.tm_clone_table))
. = ALIGN(2);
PROVIDE (_etext = .);
} > FRAM
.lodict :
{
. = ALIGN(1024);
*(.lodict)
} > FRAM
.info (NOLOAD) : {} > INFOMEM /* MSP430 INFO FLASH MEMORY SEGMENTS */
/* The rest are all not normally part of the runtime image. */
.MSP430.attributes 0 :
{
KEEP (*(.MSP430.attributes))
KEEP (*(.gnu.attributes))
KEEP (*(__TI_build_attributes))
}
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1. */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions. */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2. */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2. */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions. */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF Extension. */
.debug_macro 0 : { *(.debug_macro) }
/DISCARD/ : { *(.note.GNU-stack .debug_loclists) }
}
/****************************************************************************/
/* Include peripherals memory map */
/****************************************************************************/
INCLUDE msp430fr2476_symbols.ld

File diff suppressed because it is too large Load Diff

@ -1,315 +0,0 @@
/* ============================================================================ */
/* Copyright (c) 2021, Texas Instruments Incorporated */
/* All rights reserved. */
/* */
/* Redistribution and use in source and binary forms, with or without */
/* modification, are permitted provided that the following conditions */
/* are met: */
/* */
/* * Redistributions of source code must retain the above copyright */
/* notice, this list of conditions and the following disclaimer. */
/* */
/* * Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the */
/* documentation and/or other materials provided with the distribution. */
/* */
/* * Neither the name of Texas Instruments Incorporated nor the names of */
/* its contributors may be used to endorse or promote products derived */
/* from this software without specific prior written permission. */
/* */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" */
/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, */
/* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR */
/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, */
/* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; */
/* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */
/* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR */
/* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
/* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/* ============================================================================ */
/* This file supports MSP430G2553 devices. */
/* Version: 1.212 */
/* Default linker script, for normal executables */
OUTPUT_ARCH(msp430)
ENTRY(_start)
MEMORY {
SFR : ORIGIN = 0x0000, LENGTH = 0x0010 /* END=0x0010, size 16 */
RAM : ORIGIN = 0x0200, LENGTH = 0x0200 /* END=0x03FF, size 512 */
INFOMEM : ORIGIN = 0x1000, LENGTH = 0x0100 /* END=0x10FF, size 256 as 4 64-byte segments */
INFOA : ORIGIN = 0x10C0, LENGTH = 0x0040 /* END=0x10FF, size 64 */
INFOB : ORIGIN = 0x1080, LENGTH = 0x0040 /* END=0x10BF, size 64 */
INFOC : ORIGIN = 0x1040, LENGTH = 0x0040 /* END=0x107F, size 64 */
INFOD : ORIGIN = 0x1000, LENGTH = 0x0040 /* END=0x103F, size 64 */
ROM (rx) : ORIGIN = 0xC000, LENGTH = 0x3FDE /* END=0xFFDD, size 16350 */
BSLSIGNATURE : ORIGIN = 0xFFDE, LENGTH = 0x0002
VECT1 : ORIGIN = 0xFFE0, LENGTH = 0x0002
VECT2 : ORIGIN = 0xFFE2, LENGTH = 0x0002
VECT3 : ORIGIN = 0xFFE4, LENGTH = 0x0002
VECT4 : ORIGIN = 0xFFE6, LENGTH = 0x0002
VECT5 : ORIGIN = 0xFFE8, LENGTH = 0x0002
VECT6 : ORIGIN = 0xFFEA, LENGTH = 0x0002
VECT7 : ORIGIN = 0xFFEC, LENGTH = 0x0002
VECT8 : ORIGIN = 0xFFEE, LENGTH = 0x0002
VECT9 : ORIGIN = 0xFFF0, LENGTH = 0x0002
VECT10 : ORIGIN = 0xFFF2, LENGTH = 0x0002
VECT11 : ORIGIN = 0xFFF4, LENGTH = 0x0002
VECT12 : ORIGIN = 0xFFF6, LENGTH = 0x0002
VECT13 : ORIGIN = 0xFFF8, LENGTH = 0x0002
VECT14 : ORIGIN = 0xFFFA, LENGTH = 0x0002
VECT15 : ORIGIN = 0xFFFC, LENGTH = 0x0002
RESETVEC : ORIGIN = 0xFFFE, LENGTH = 0x0002
}
SECTIONS
{
.bslsignature : {} > BSLSIGNATURE
__interrupt_vector_1 : { KEEP (*(__interrupt_vector_1 )) KEEP (*(__interrupt_vector_trapint)) } > VECT1
__interrupt_vector_2 : { KEEP (*(__interrupt_vector_2 )) } > VECT2
__interrupt_vector_3 : { KEEP (*(__interrupt_vector_3 )) KEEP (*(__interrupt_vector_port1)) } > VECT3
__interrupt_vector_4 : { KEEP (*(__interrupt_vector_4 )) KEEP (*(__interrupt_vector_port2)) } > VECT4
__interrupt_vector_5 : { KEEP (*(__interrupt_vector_5 )) } > VECT5
__interrupt_vector_6 : { KEEP (*(__interrupt_vector_6 )) KEEP (*(__interrupt_vector_adc10)) } > VECT6
__interrupt_vector_7 : { KEEP (*(__interrupt_vector_7 )) KEEP (*(__interrupt_vector_usciab0tx)) } > VECT7
__interrupt_vector_8 : { KEEP (*(__interrupt_vector_8 )) KEEP (*(__interrupt_vector_usciab0rx)) } > VECT8
__interrupt_vector_9 : { KEEP (*(__interrupt_vector_9 )) KEEP (*(__interrupt_vector_timer0_a1)) } > VECT9
__interrupt_vector_10 : { KEEP (*(__interrupt_vector_10)) KEEP (*(__interrupt_vector_timer0_a0)) } > VECT10
__interrupt_vector_11 : { KEEP (*(__interrupt_vector_11)) KEEP (*(__interrupt_vector_wdt)) } > VECT11
__interrupt_vector_12 : { KEEP (*(__interrupt_vector_12)) KEEP (*(__interrupt_vector_comparatora)) } > VECT12
__interrupt_vector_13 : { KEEP (*(__interrupt_vector_13)) KEEP (*(__interrupt_vector_timer1_a1)) } > VECT13
__interrupt_vector_14 : { KEEP (*(__interrupt_vector_14)) KEEP (*(__interrupt_vector_timer1_a0)) } > VECT14
__interrupt_vector_15 : { KEEP (*(__interrupt_vector_15)) KEEP (*(__interrupt_vector_nmi)) } > VECT15
__reset_vector :
{
KEEP (*(__interrupt_vector_16))
KEEP (*(__interrupt_vector_reset))
KEEP (*(.resetvec))
} > RESETVEC
.rodata :
{
. = ALIGN(2);
*(.plt)
*(.rodata .rodata.* .gnu.linkonce.r.* .const .const:*)
*(.rodata1)
KEEP (*(.gcc_except_table)) *(.gcc_except_table.*)
} > ROM
/* Note: This is a separate .rodata section for sections which are
read only but which older linkers treat as read-write.
This prevents older linkers from marking the entire .rodata
section as read-write. */
.rodata2 :
{
. = ALIGN(2);
PROVIDE (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE (__preinit_array_end = .);
. = ALIGN(2);
PROVIDE (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE (__init_array_end = .);
. = ALIGN(2);
PROVIDE (__fini_array_start = .);
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
PROVIDE (__fini_array_end = .);
/* . = ALIGN(2);
*(.eh_frame_hdr)
KEEP (*(.eh_frame)) */
/* gcc uses crtbegin.o to find the start of the constructors, so
we make sure it is first. Because this is a wildcard, it
doesn't matter if the user does not actually link against
crtbegin.o; the linker won't look for a file to match a
wildcard. The wildcard also means that it doesn't matter which
directory crtbegin.o is in. */
KEEP (*crtbegin*.o(.ctors))
/* We don't want to include the .ctor section from the crtend.o
file until after the sorted ctors. The .ctor section from
the crtend file contains the end of ctors marker and it must
be last */
KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
KEEP (*crtbegin*.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
} > ROM
.text :
{
. = ALIGN(2);
PROVIDE (_start = .);
KEEP (*(SORT(.crt_*)))
*(.lowtext .text .stub .text.* .gnu.linkonce.t.* .text:*)
KEEP (*(.text.*personality*))
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.interp .hash .dynsym .dynstr .gnu.version*)
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
. = ALIGN(2);
KEEP (*(.init))
KEEP (*(.fini))
KEEP (*(.tm_clone_table))
} > ROM
.data :
{
. = ALIGN(2);
PROVIDE (__datastart = .);
KEEP (*(.jcr))
*(.data.rel.ro.local) *(.data.rel.ro*)
*(.dynamic)
*(.data .data.* .gnu.linkonce.d.*)
KEEP (*(.gnu.linkonce.d.*personality*))
SORT(CONSTRUCTORS)
*(.data1)
*(.got.plt) *(.got)
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
. = ALIGN(2);
*(.sdata .sdata.* .gnu.linkonce.s.* D_2 D_1)
. = ALIGN(2);
_edata = .;
PROVIDE (edata = .);
PROVIDE (__dataend = .);
} > RAM AT>ROM
/* Note that crt0 assumes this is a multiple of two; all the
start/stop symbols are also assumed word-aligned. */
PROVIDE(__romdatastart = LOADADDR(.data));
PROVIDE (__romdatacopysize = SIZEOF(.data));
.bss :
{
. = ALIGN(2);
PROVIDE (__bssstart = .);
*(.dynbss)
*(.sbss .sbss.*)
*(.bss .bss.* .gnu.linkonce.b.*)
. = ALIGN(2);
*(COMMON)
PROVIDE (__bssend = .);
} > RAM
PROVIDE (__bsssize = SIZEOF(.bss));
/* This section contains data that is not initialised during load
or application reset. */
.noinit (NOLOAD) :
{
. = ALIGN(2);
PROVIDE (__noinit_start = .);
*(.noinit)
. = ALIGN(2);
PROVIDE (__noinit_end = .);
end = .;
} > RAM
/* We create this section so that "end" will always be in the
RAM region (matching .stack below), even if the .bss
section is empty. */
.heap (NOLOAD) :
{
. = ALIGN(2);
__heap_start__ = .;
_end = __heap_start__;
PROVIDE (end = .);
KEEP (*(.heap))
_end = .;
PROVIDE (end = .);
/* This word is here so that the section is not empty, and thus
not discarded by the linker. The actual value does not matter
and is ignored. */
LONG(0);
__heap_end__ = .;
__HeapLimit = __heap_end__;
} > RAM
/* WARNING: Do not place anything in RAM here.
The heap section must be the last section in RAM and the stack
section must be placed at the very end of the RAM region. */
.stack (ORIGIN (RAM) + LENGTH(RAM)) :
{
PROVIDE (__stack = .);
*(.stack)
}
.infoA : {} > INFOA /* MSP430 INFO FLASH MEMORY SEGMENTS */
.infoB : {} > INFOB
.infoC : {} > INFOC
.infoD : {} > INFOD
/* The rest are all not normally part of the runtime image. */
.MSP430.attributes 0 :
{
KEEP (*(.MSP430.attributes))
KEEP (*(.gnu.attributes))
KEEP (*(__TI_build_attributes))
}
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1. */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions. */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2. */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2. */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions. */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF Extension. */
.debug_macro 0 : { *(.debug_macro) }
/DISCARD/ : { *(.note.GNU-stack) }
}
/****************************************************************************/
/* Include peripherals memory map */
/****************************************************************************/
INCLUDE msp430g2553_symbols.ld

@ -19,7 +19,7 @@
#ifndef ALEEFORTH_SPLITMEMDICT_HPP
#define ALEEFORTH_SPLITMEMDICT_HPP
#include "alee.hpp"
#include "libalee/alee.hpp"
#include <algorithm>
@ -35,6 +35,7 @@ class SplitMemDict : public Dictionary
uint8_t rwdict[MemDictSize - Dictionary::Begin] = {0};
uint8_t extra[Dictionary::Begin];
LIBALEE_SECTION
Addr convertAddress(Addr addr) const noexcept {
return addr < RON ? addr : static_cast<Addr>(addr - RON);
}
@ -53,6 +54,7 @@ public:
return *this;
}
LIBALEE_SECTION
virtual Cell read(Addr addr) const noexcept final {
const uint8_t *dict;
if (addr < RON)
@ -63,6 +65,7 @@ public:
return *reinterpret_cast<const Cell *>(dict + convertAddress(addr));
}
LIBALEE_SECTION
virtual void write(Addr addr, Cell value) noexcept final {
if (addr >= RON)
*reinterpret_cast<Cell *>(rwdict + addr - RON) = value;
@ -70,6 +73,7 @@ public:
*reinterpret_cast<Cell *>(extra + addr) = value;
}
LIBALEE_SECTION
virtual uint8_t readbyte(Addr addr) const noexcept final {
const uint8_t *dict;
if (addr < RON)
@ -80,6 +84,7 @@ public:
return dict[convertAddress(addr)];
}
LIBALEE_SECTION
virtual void writebyte(Addr addr, uint8_t value) noexcept final {
if (addr >= RON)
rwdict[addr - RON] = value;
@ -87,6 +92,7 @@ public:
extra[addr] = value;
}
LIBALEE_SECTION
virtual unsigned long int capacity() const noexcept final {
return RON + sizeof(extra) + sizeof(rwdict);
}

@ -0,0 +1,80 @@
/**
* Alee Forth: A portable and concise Forth implementation in modern C++.
* Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef ALEEFORTH_SPLITMEMDICTRW_HPP
#define ALEEFORTH_SPLITMEMDICTRW_HPP
#include "libalee/alee.hpp"
#include <algorithm>
template<unsigned long int LON, unsigned long int HIN>
class SplitMemDictRW : public Dictionary
{
uint8_t *lodict;
uint32_t hidict;
public:
constexpr explicit SplitMemDictRW(uint8_t *lo, uint32_t hi):
lodict(lo), hidict(hi) {}
constexpr SplitMemDictRW(const SplitMemDictRW<LON, HIN>& spd):
SplitMemDictRW(spd.lodict, spd.hidict) {}
constexpr auto& operator=(const SplitMemDictRW<LON, HIN>& spd) {
*this = SplitMemDictRW(spd.lodict, hidict);
return *this;
}
virtual Cell read(Addr addr) const noexcept final {
if (addr < LON)
return *reinterpret_cast<const Cell *>(lodict + addr);
else
return _data20_read_short(hidict + addr - LON);
}
virtual void write(Addr addr, Cell value) noexcept final {
if (addr < LON)
*reinterpret_cast<Cell *>(lodict + addr) = value;
else
_data20_write_short(hidict + addr - LON, value);
}
virtual uint8_t readbyte(Addr addr) const noexcept final {
if (addr < LON)
return lodict[addr];
else
return _data20_read_char(hidict + addr - LON);
}
virtual void writebyte(Addr addr, uint8_t value) noexcept final {
if (addr < LON)
lodict[addr] = value;
else
_data20_write_char(hidict + addr - LON, value);
}
virtual unsigned long int capacity() const noexcept final {
return LON + HIN;
}
private:
virtual ~SplitMemDictRW() override {};
};
#endif // ALEEFORTH_SPLITMEMDICTRW_HPP

@ -0,0 +1 @@
Subproject commit 45b7ecc1e37d8fdd1bd9a29ec52a15bf7ac69d7f
Loading…
Cancel
Save