Compare commits

..

3 Commits

1
.gitattributes vendored

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

1
.gitignore vendored

@ -8,6 +8,5 @@ alee-msp430
alee-standalone alee-standalone
libalee.a libalee.a
core.fth.h core.fth.h
doc/
msp430/lzss msp430/lzss
msp430/msp430fr2476_all.h msp430/msp430fr2476_all.h

2303
Doxyfile

File diff suppressed because it is too large Load Diff

@ -15,12 +15,9 @@ msp430: AR := msp430-elf-gcc-ar
msp430: CXXFLAGS += -I. -I/usr/msp430-elf/usr/include msp430: CXXFLAGS += -I. -I/usr/msp430-elf/usr/include
msp430: CXXFLAGS += -Os -mmcu=msp430fr2476 -ffunction-sections -fdata-sections msp430: CXXFLAGS += -Os -mmcu=msp430fr2476 -ffunction-sections -fdata-sections
msp430: CXXFLAGS += -flto -fno-asynchronous-unwind-tables -fno-threadsafe-statics -fno-stack-protector 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
msp430: LDFLAGS += -L msp430 -T msp430fr2476.ld -Wl,-gc-sections -Wl,--no-warn-rwx-segments
msp430: msp430/alee-msp430 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: STANDALONE += forth/core-ext.fth forth/tools.fth forth/msp430.fth
msp430-prep: core.fth.h msp430-prep: core.fth.h
msp430-prep: clean-lib msp430-prep: clean-lib
@ -31,12 +28,11 @@ small: alee
fast: CXXFLAGS += -O3 -march=native -mtune=native -flto fast: CXXFLAGS += -O3 -march=native -mtune=native -flto
fast: alee fast: alee
standalone: core.fth.h
standalone: alee-standalone standalone: alee-standalone
alee: $(LIBFILE) alee: $(LIBFILE)
msp430/alee-msp430: $(LIBFILE) msp430/alee-msp430: $(LIBFILE)
alee-standalone: $(LIBFILE) alee-standalone: core.fth.h $(LIBFILE)
cppcheck: cppcheck:
cppcheck --enable=warning,style,information --disable=missingInclude \ cppcheck --enable=warning,style,information --disable=missingInclude \
@ -50,14 +46,11 @@ $(LIBFILE): $(OBJFILES)
core.fth.h: alee.dat core.fth.h: alee.dat
xxd -i $< > $@ xxd -i $< > $@
sed -i "s/\[\]/\[ALEE_RODICTSIZE\]/" $@ sed -i "s/unsigned /static &/" $@
alee.dat: alee $(STANDALONE) alee.dat: alee $(STANDALONE)
echo "3 sys" | ./alee $(STANDALONE) echo "3 sys" | ./alee $(STANDALONE)
msp430/msp430fr2476_all.h:
$(MAKE) -C msp430
clean: clean-lib clean: clean-lib
rm -f alee alee-standalone msp430/alee-msp430 rm -f alee alee-standalone msp430/alee-msp430
rm -f alee.dat core.fth.h rm -f alee.dat core.fth.h

@ -16,16 +16,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "libalee/alee.hpp" #include "alee.hpp"
#include "splitmemdict.hpp" #include "splitmemdict.hpp"
#include <array>
#include <charconv> #include <charconv>
#include <chrono>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#define ALEE_RODICTSIZE alignas(sizeof(Cell))
#include "core.fth.h" #include "core.fth.h"
static bool okay = false; static bool okay = false;
@ -84,6 +84,8 @@ static void load(State& state)
void user_sys(State& state) void user_sys(State& state)
{ {
static bool start = false;
static decltype(std::chrono::high_resolution_clock::now()) last;
char buf[32] = {0}; char buf[32] = {0};
switch (state.pop()) { switch (state.pop()) {
@ -104,6 +106,20 @@ void user_sys(State& state)
case 4: // load case 4: // load
load(state); load(state);
break; break;
case 5: // time
if (!start) {
start = true;
last = std::chrono::high_resolution_clock::now();
} else {
start = false;
auto diff = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now() - last);
state.push((Cell)diff.count());
}
break;
case 6: // double-add
{ auto sum = state.popd() + state.popd(); state.pushd(sum); }
break;
default: default:
break; break;
} }

@ -16,21 +16,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "libalee/alee.hpp" #include "alee.hpp"
#include "memdict.hpp" #include "memdict.hpp"
#include <charconv> #include <charconv>
#include <chrono>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#ifdef ALEE_MSP430
#include "lzss.h"
static const
#include "msp430fr2476_all.h"
static Error findword(State&, Word);
#endif // ALEE_MSP430
static bool okay = false; static bool okay = false;
static void readchar(State&); static void readchar(State&);
@ -41,9 +35,6 @@ int main(int argc, char *argv[])
{ {
MemDict dict; MemDict dict;
State state (dict, readchar); State state (dict, readchar);
#ifdef ALEE_MSP430
Parser::customParse = findword;
#endif // ALEE_MSP430
dict.initialize(); dict.initialize();
@ -92,6 +83,8 @@ static void load(State& state)
void user_sys(State& state) void user_sys(State& state)
{ {
static std::chrono::time_point<std::chrono::high_resolution_clock> last;
static bool start = false;
char buf[32] = {0}; char buf[32] = {0};
switch (state.pop()) { switch (state.pop()) {
@ -112,6 +105,20 @@ void user_sys(State& state)
case 4: // load case 4: // load
load(state); load(state);
break; break;
case 5: // time
if (!start) {
start = true;
last = std::chrono::high_resolution_clock::now();
} else {
start = false;
auto diff = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now() - last);
state.push((Cell)diff.count());
}
break;
case 6: // double-add
{ auto sum = state.popd() + state.popd(); state.pushd(sum); }
break;
default: default:
break; break;
} }
@ -163,48 +170,3 @@ void parseFile(State& state, std::istream& file)
} }
} }
#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)
{
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;
}
}
return 0;
});
if (ret == EOF) {
return Error::noword;
} else {
Parser::processLiteral(state, (Cell)lzwlen);
return Error::none;
}
}
#endif // ALEE_MSP430

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

@ -67,7 +67,7 @@
drop - spaces u. ; drop - spaces u. ;
( WORD uses HERE and must be at least 33 characters. ) ( WORD uses HERE and must be at least 33 characters. )
: pad here [ 50 chars ] literal + align ; : pad here 50 chars + align ;
: parse here dup >r swap begin : parse here dup >r swap begin
key? if key else dup then 2dup <> while key? if key else dup then 2dup <> while

@ -6,8 +6,6 @@
: cell+ 2 + ; : cell+ 2 + ;
: cells 2 * ; : cells 2 * ;
: char+ 1 + ;
: chars ;
: . 0 sys ; : . 0 sys ;
: emit 2 sys ; : emit 2 sys ;
@ -15,48 +13,50 @@
: 1+ 1 + ; : 1+ 1 + ;
: 1- 1 - ; : 1- 1 - ;
: over 1 pick ;
: rot >r swap r> swap ;
: -rot rot rot ;
: ' _' drop ; : ' _' drop ;
: ! 1 _! ; : ! 1 _! ;
: @ 1 _@ ; : @ 1 _@ ;
: +! dup >r swap r> @ + swap ! ; : +! dup >r swap r> @ + swap ! ;
: base 0 ;
: here 1 cells @ ;
: allot 1 cells +! ;
: c! 0 _! ;
: c@ 0 _@ ;
: c, here c! 1 allot ;
: char+ 1+ ;
: chars ;
: _latest 2 cells ; : _latest 2 cells ;
: imm _latest @ dup @ 1 5 << | swap ! ; : imm _latest @ dup @ 1 5 << | swap ! ;
: immediate imm ; : immediate imm ;
: state 3 cells ;
: _compxt 4 cells ;
: _source 5 cells ;
: _sourceu 6 cells ;
: >in 7 cells ;
: _begin 8 cells 80 chars + ;
: [ 0 3 cells ! ; imm : , here ! 1 cells allot ;
: ] 1 3 cells ! ;
: , 1 cells dup >r @ ! r> dup +! ; : [ 0 state ! ; imm
: ] 1 state ! ;
: literal [ ' _lit dup , , ] , , ; imm : literal [ ' _lit dup , , ] , , ; imm
: ['] ' [ ' literal , ] ; 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 : if ['] _jmp0 , here 0 , ; imm
: then here swap ! ; imm : then here swap ! ; imm
: else ['] _jmp , here 0 , swap here swap ! ; imm : else ['] _jmp , here 0 , swap here swap ! ; imm
: postpone _' dup 0 = if exit then : postpone _' dup 0 = if exit then
1 = swap ['] _lit , , if ['] execute , 1 = swap ['] _lit , , if ['] execute
else ['] , , then ; imm else ['] , then , ; imm
: over 1 pick ;
: rot >r swap r> swap ;
: -rot rot rot ;
: 2drop drop drop ; : 2drop drop drop ;
: 2dup over over ; : 2dup over over ;
@ -99,8 +99,7 @@
: j postpone 2r> ['] r> , postpone r@ ['] swap , : j postpone 2r> ['] r> , postpone r@ ['] swap ,
['] >r , ['] -rot , postpone 2>r ; imm ['] >r , ['] -rot , postpone 2>r ; imm
: aligned dup [ 1 cells 1- ] literal swap over & if [ 1 cells ] literal : aligned dup 1 cells 1- swap over & if 1 cells swap - + else drop then ;
swap - + else drop then ;
: align here dup aligned swap - allot ; : align here dup aligned swap - allot ;
: and & ; : and & ;
@ -111,7 +110,7 @@
: invert -1 ^ ; : invert -1 ^ ;
: mod % ; : mod % ;
: 2* 2 * ; : 2* 2 * ;
: _msb [ 1 1 cells 8 * 1- << ] literal ; : _msb 1 1 cells 8 * 1- << ;
: 2/ dup 1 >> swap 0< if _msb or then ; : 2/ dup 1 >> swap 0< if _msb or then ;
: /mod 2dup % -rot / ; : /mod 2dup % -rot / ;
@ -146,14 +145,12 @@
2dup <> while rot repeat 2dup <> while rot repeat
2drop here - here c! here ; 2drop here - here c! here ;
: count dup char+ swap c@ ; : count dup char+ swap c@ ;
: char 0 here char+ c! bl word char+ c@ ; : char bl word char+ c@ ;
: [char] char postpone literal ; imm : [char] char postpone literal ; imm
: ( begin [char] ) key <> while repeat ; imm : ( begin [char] ) key <> while repeat ; imm
: _type >r begin dup 0 > while : type begin dup 0 > while swap dup c@ emit char+ swap 1- repeat 2drop ;
swap dup c@ r@ execute char+ swap 1- repeat 2drop r> drop ;
: type [ ' emit ] literal _type ;
: s" state @ if ['] _jmp , here 0 , then : s" state @ if ['] _jmp , here 0 , then
[char] " word count [char] " word count
state @ 0= if exit then state @ 0= if exit then
@ -164,17 +161,17 @@
: :noname here dup _compxt ! 0 , here swap ] ; : :noname here dup _compxt ! 0 , here swap ] ;
: create : here [ 4 cells ] literal + postpone literal postpone ; 0 , ; : create : here 4 cells + postpone literal postpone ; 0 , ;
: >body cell+ @ ; : >body cell+ @ ;
: _does> >r _latest @ dup @ 31 & + cell+ aligned [ 2 cells ] literal + : _does> >r _latest @ dup @ 31 & + cell+ aligned 2 cells +
['] _jmp over ! cell+ r> cell+ swap ! ; ['] _jmp over ! cell+ r> cell+ swap ! ;
: does> state @ if : does> state @ if
['] _lit , here 2 cells + , ['] _does> , ['] exit , else ['] _lit , here 2 cells + , ['] _does> , ['] exit , else
here dup _does> dup _compxt ! 0 , ] then ; imm here dup _does> dup _compxt ! 0 , ] then ; imm
: variable create [ 1 cells ] literal allot ; : variable create 1 cells allot ;
: constant create , does> @ ; : constant create , does> @ ;
: quit begin _rdepth 1 > while r> drop repeat postpone [ ; : quit begin _rdepth 1 > while r> drop repeat postpone [ ;
@ -200,7 +197,7 @@
: accept over >r begin dup 0 > while : accept over >r begin dup 0 > while
key dup 32 < if 2drop 0 key dup 32 < if 2drop 0
else dup emit rot 2dup c! char+ swap drop swap 1- then else dup emit rot 2dup c! char+ swap drop swap 1- then
repeat drop r> - [ 1 chars ] literal / ; repeat drop r> - 1 chars / ;
: evaluate _source @ >r _sourceu @ >r >in @ >r : evaluate _source @ >r _sourceu @ >r >in @ >r
0 >in ! _sourceu ! _source ! _ev 0 >in ! _sourceu ! _source ! _ev

@ -0,0 +1,19 @@
: fib ( n -- d )
>r 0 dup 1 0 r> 0 do
2dup 2>r 2swap 6 sys 2r> 2swap loop 2drop ;
: fibtest ( n -- )
0 do i fib <# #s #> type space loop ;
: fibbench ( n -- )
5 sys fib 5 sys >r 2drop r> ;
variable avg 0 avg !
1000 constant iters
: bench ( -- )
iters 0 do 1000 fibbench avg +! loop
avg @ iters / avg ! ;
bench ." avg time: " avg @ . ." us" cr
bye

@ -1,97 +1,18 @@
: vector! 10 sys ; : vector! 10 sys ;
: byte! 11 sys ; : reg! 11 sys ;
: byte@ 12 sys ; : reg@ 12 sys ;
: reg! 13 sys ; : 2reg! 13 sys ;
: reg@ 14 sys ; : 2reg@ 14 sys ;
: sr+ 15 sys ; : sr+ 15 sys ;
: sr- 16 sys ; : sr- 16 sys ;
: lpm-exit 17 sys ;
: reg [ ' reg@ ' reg! ] literal literal ; : reg [ ' reg@ ' reg! ] literal literal ;
: byte [ ' byte@ ' byte! ] literal literal ; : 2reg [ ' 2reg@ ' 2reg! ] literal literal ;
: set ( b r reg/byte -- ) : set ( b r reg/wreg -- )
>r over r> execute >r rot r> | -rot execute ; >r over r> execute >r rot r> | -rot execute ;
: clear ( b r reg/byte -- ) : clear ( b r reg/wreg -- )
>r over r> execute >r rot invert r> & -rot execute ; >r over r> execute >r rot invert r> & -rot execute ;
: toggle ( b r reg/byte -- ) : toggle ( b r reg/wreg -- )
>r over r> execute >r rot r> ^ -rot execute ; >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 ;

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

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

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

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

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

@ -16,9 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "alee.hpp" #include "dictionary.hpp"
#include <cstring>
LIBALEE_SECTION
void Dictionary::initialize() void Dictionary::initialize()
{ {
write(Base, 10); write(Base, 10);
@ -28,7 +29,6 @@ void Dictionary::initialize()
write(Source, Input + sizeof(Cell)); write(Source, Input + sizeof(Cell));
} }
LIBALEE_SECTION
Addr Dictionary::allot(Cell amount) noexcept Addr Dictionary::allot(Cell amount) noexcept
{ {
Addr old = here(); Addr old = here();
@ -37,38 +37,34 @@ Addr Dictionary::allot(Cell amount) noexcept
if (neww < capacity()) { if (neww < capacity()) {
write(Here, static_cast<Addr>(neww)); write(Here, static_cast<Addr>(neww));
} else { } else {
// TODO how to handle allot failure? Error code? // TODO
} }
return old; return old;
} }
LIBALEE_SECTION
void Dictionary::add(Cell value) noexcept void Dictionary::add(Cell value) noexcept
{ {
write(allot(sizeof(Cell)), value); write(allot(sizeof(Cell)), value);
} }
LIBALEE_SECTION
Addr Dictionary::aligned(Addr addr) Addr Dictionary::aligned(Addr addr)
{ {
return (addr + (sizeof(Cell) - 1)) & ~(sizeof(Cell) - 1); return (addr + (sizeof(Cell) - 1)) & ~(sizeof(Cell) - 1);
} }
LIBALEE_SECTION
Addr Dictionary::alignhere() noexcept Addr Dictionary::alignhere() noexcept
{ {
here(aligned(here())); here(aligned(here()));
return here(); return here();
} }
LIBALEE_SECTION
void Dictionary::addDefinition(Word word) noexcept void Dictionary::addDefinition(Word word) noexcept
{ {
Cell wsize = word.size(); Cell wsize = word.size();
add(wsize); add(wsize);
if (alignhere() - latest() >= MaxDistance) if (alignhere() - latest() >= ((1 << (sizeof(Cell) * 8 - 6)) - 1))
add(0); add(0);
auto addr = allot(wsize); auto addr = allot(wsize);
@ -81,7 +77,6 @@ void Dictionary::addDefinition(Word word) noexcept
alignhere(); alignhere();
} }
LIBALEE_SECTION
Addr Dictionary::find(Word word) noexcept Addr Dictionary::find(Word word) noexcept
{ {
Addr lt = latest(); Addr lt = latest();
@ -91,7 +86,7 @@ Addr Dictionary::find(Word word) noexcept
const Addr len = l & 0x1F; const Addr len = l & 0x1F;
Word lw; Word lw;
if ((l >> 6) < MaxDistance) { if ((l >> 6) < 1023) {
lw = Word::fromLength(lt + sizeof(Cell), len); lw = Word::fromLength(lt + sizeof(Cell), len);
if (equal(word, lw)) if (equal(word, lw))
return lt; return lt;
@ -113,26 +108,24 @@ Addr Dictionary::find(Word word) noexcept
return 0; return 0;
} }
LIBALEE_SECTION
Addr Dictionary::getexec(Addr addr) noexcept Addr Dictionary::getexec(Addr addr) noexcept
{ {
const Addr l = read(addr); const Addr l = read(addr);
const Addr len = l & 0x1Fu; const Addr len = l & 0x1Fu;
addr += sizeof(Cell); addr += sizeof(Cell);
if ((l >> 6) == MaxDistance) if ((l >> 6) == 1023)
addr += sizeof(Cell); addr += sizeof(Cell);
addr += len; addr += len;
return aligned(addr); return aligned(addr);
} }
LIBALEE_SECTION
bool Dictionary::hasInput() const noexcept bool Dictionary::hasInput() const noexcept
{ {
const Addr src = read(Dictionary::Source); const Addr src = read(Dictionary::Source);
const Addr end = read(Dictionary::SourceLen); const Addr end = read(Dictionary::SourceLen);
auto idx = static_cast<uint8_t>(read(Dictionary::Input)); uint8_t idx = read(Dictionary::Input) & 0xFFu;
while (idx < end) { while (idx < end) {
auto ch = readbyte(src + idx); auto ch = readbyte(src + idx);
@ -149,12 +142,11 @@ bool Dictionary::hasInput() const noexcept
return false; return false;
} }
LIBALEE_SECTION
Word Dictionary::input() noexcept Word Dictionary::input() noexcept
{ {
const Addr src = read(Dictionary::Source); const Addr src = read(Dictionary::Source);
const Addr end = read(Dictionary::SourceLen); const Addr end = read(Dictionary::SourceLen);
auto idx = static_cast<uint8_t>(read(Dictionary::Input)); uint8_t idx = read(Dictionary::Input) & 0xFFu;
Addr wstart = src + idx; Addr wstart = src + idx;
Addr wend = wstart; Addr wend = wstart;
@ -179,13 +171,11 @@ Word Dictionary::input() noexcept
return Word(wstart, wend); return Word(wstart, wend);
} }
LIBALEE_SECTION
bool Dictionary::equal(Word word, const char *str, unsigned len) const noexcept 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 bool Dictionary::equal(Word word, Word other) const noexcept
{ {
return word.size() == other.size() && equal(word.begin(this), word.end(this), other.begin(this)); return word.size() == other.size() && equal(word.begin(this), word.end(this), other.begin(this));

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

@ -16,33 +16,33 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "alee.hpp" #include "corewords.hpp"
#include "ctype.hpp"
#include "parser.hpp"
#include <algorithm>
#include <cstring>
Error (*Parser::customParse)(State&, Word) = nullptr; Error (*Parser::customParse)(State&, Word) = nullptr;
LIBALEE_SECTION
Error Parser::parse(State& state, const char *str) Error Parser::parse(State& state, const char *str)
{ {
auto addr = Dictionary::Input; auto addr = Dictionary::Input;
// Set source and input length const auto len = static_cast<Cell>(std::strlen(str));
const auto len = static_cast<Cell>(strlen(str));
state.dict.write(addr, 0); state.dict.write(addr, 0);
state.dict.write(Dictionary::SourceLen, len); state.dict.write(Dictionary::SourceLen, len);
// Fill input buffer with string contents
addr += sizeof(Cell); addr += sizeof(Cell);
while (*str) while (*str)
state.dict.writebyte(addr++, *str++); state.dict.writebyte(addr++, *str++);
// Zero the remaining input buffer
while (addr < Dictionary::Input + Dictionary::InputCells) while (addr < Dictionary::Input + Dictionary::InputCells)
state.dict.writebyte(addr++, '\0'); state.dict.writebyte(addr++, '\0');
return parseSource(state); return parseSource(state);
} }
LIBALEE_SECTION
Error Parser::parseSource(State& state) Error Parser::parseSource(State& state)
{ {
auto err = Error::none; auto err = Error::none;
@ -53,14 +53,11 @@ Error Parser::parseSource(State& state)
return err; return err;
} }
LIBALEE_SECTION
Error Parser::parseWord(State& state, Word word) Error Parser::parseWord(State& state, Word word)
{ {
bool imm; bool imm;
Addr ins; Addr ins = state.dict.find(word);
// Search order: dictionary, core word-set, number, custom parse.
ins = state.dict.find(word);
if (ins == 0) { if (ins == 0) {
auto cw = CoreWords::findi(state, word); auto cw = CoreWords::findi(state, word);
@ -72,7 +69,7 @@ Error Parser::parseWord(State& state, Word word)
return r; return r;
} else { } else {
ins = cw; ins = cw;
imm = ins == CoreWords::token(";"); imm = ins == CoreWords::Semicolon;
} }
} else { } else {
imm = state.dict.read(ins) & Dictionary::Immediate; imm = state.dict.read(ins) & Dictionary::Immediate;
@ -87,7 +84,6 @@ Error Parser::parseWord(State& state, Word word)
return Error::none; return Error::none;
} }
LIBALEE_SECTION
Error Parser::parseNumber(State& state, Word word) Error Parser::parseNumber(State& state, Word word)
{ {
const auto base = state.dict.read(Dictionary::Base); const auto base = state.dict.read(Dictionary::Base);
@ -99,7 +95,7 @@ Error Parser::parseNumber(State& state, Word word)
++it; ++it;
const auto end = word.end(&state.dict); const auto end = word.end(&state.dict);
for (uint8_t c; it != end; ++it) { for (char c; it != end; ++it) {
c = *it; c = *it;
if (isdigit(c)) { if (isdigit(c)) {
@ -120,15 +116,11 @@ Error Parser::parseNumber(State& state, Word word)
return Error::none; return Error::none;
} }
LIBALEE_SECTION
void Parser::processLiteral(State& state, Cell value) void Parser::processLiteral(State& state, Cell value)
{ {
if (state.compiling()) { if (state.compiling()) {
constexpr auto ins = CoreWords::token("_lit"); constexpr auto ins = CoreWords::findi("_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; const Cell maxlit = Dictionary::Begin - CoreWords::WordCount;
if (value >= 0 && value < maxlit) if (value >= 0 && value < maxlit)
value += CoreWords::WordCount; value += CoreWords::WordCount;

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

@ -16,46 +16,65 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "alee.hpp" #include "corewords.hpp"
#include "state.hpp"
#include <cstring>
#include <iterator> #include <iterator>
LIBALEE_SECTION
bool State::compiling() const bool State::compiling() const
{ {
return dict.read(Dictionary::Compiling); return dict.read(Dictionary::Compiling);
} }
LIBALEE_SECTION
void State::compiling(bool yes) void State::compiling(bool yes)
{ {
dict.write(Dictionary::Compiling, yes); dict.write(Dictionary::Compiling, yes);
} }
LIBALEE_SECTION
State::Context State::save() State::Context State::save()
{ {
return context; return context;
} }
LIBALEE_SECTION
void State::load(const State::Context& ctx) void State::load(const State::Context& ctx)
{ {
context = ctx; context = ctx;
} }
LIBALEE_SECTION void State::execute1(Addr ins)
{
repeat:
if (ins >= Dictionary::Begin) [[likely]] {
// Subroutine call
pushr(context.ip);
context.ip = ins;
} else {
if (ins < CoreWords::WordCount) {
if (CoreWords::run(ins, *this)) [[unlikely]] {
ins = pop();
goto repeat;
}
} else {
push(static_cast<Cell>(ins - CoreWords::WordCount));
}
context.ip += sizeof(Cell);
}
}
Error State::execute(Addr addr) Error State::execute(Addr addr)
{ {
auto stat = static_cast<Error>(setjmp(context.jmpbuf)); auto stat = static_cast<Error>(setjmp(context.jmpbuf));
if (stat == Error::none) { if (stat == Error::none) {
CoreWords::run(addr, *this); context.ip = 0;
execute1(addr);
if (context.ip >= Dictionary::Begin) { if (context.ip >= Dictionary::Begin) {
// longjmp will exit this loop. // longjmp will exit this loop.
for (;;) for (;;)
CoreWords::run(dict.read(context.ip), *this); execute1(dict.read(context.ip));
} else { } else {
// addr was a CoreWord, all done now. // addr was a CoreWord, all done now.
context.ip = 0; context.ip = 0;
@ -67,7 +86,6 @@ Error State::execute(Addr addr)
return stat; return stat;
} }
LIBALEE_SECTION
void State::reset() void State::reset()
{ {
while (size()) while (size())
@ -79,13 +97,11 @@ void State::reset()
context.ip = 0; context.ip = 0;
} }
LIBALEE_SECTION
std::size_t State::size() const noexcept std::size_t State::size() const noexcept
{ {
return dsp - dstack; return dsp - dstack;
} }
LIBALEE_SECTION
std::size_t State::rsize() const noexcept std::size_t State::rsize() const noexcept
{ {
return rsp - rstack; return rsp - rstack;

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

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

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

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

@ -0,0 +1,12 @@
dict: lzss
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 "printf ... > msp430fr2476_all.bin"
@printf "$(shell 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
lzss: lzss.c

@ -16,63 +16,49 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "libalee/alee.hpp" #include "alee.hpp"
#include "libalee/ctype.hpp"
#include "lzss.h" #include "lzss.h"
static const static const
#include "msp430fr2476_all.h" #include "msp430fr2476_all.h"
#include <cstring>
#include <msp430.h> #include <msp430.h>
#include "splitmemdictrw.hpp" #include "splitmemdictrw.hpp"
alignas(sizeof(Cell))
__attribute__((section(".lodict")))
#include "core.fth.h"
static char strbuf[80]; static char strbuf[80];
static void readchar(State& state); static void readchar(State& state);
static void serput(int c); static void serput(int c);
static void serputs(const char *s); static void serputs(const char *s);
static void printint(DoubleCell n, char *buf, int base); static void printint(DoubleCell n, char *buf);
static Error findword(State&, Word); static Error findword(State&, Word);
static void initGPIO(); static void initGPIO();
static void initClock(); static void initClock();
static void initUART(); static void initUART();
static void Software_Trim(); static void Software_Trim();
#define MCLK_FREQ_MHZ (16) #define MCLK_FREQ_MHZ (8) // MCLK = 8MHz
static void alee_main(); //__attribute__((section(".hidict")))
//static uint8_t hidict[16384];
#define ALEE_RODICTSIZE (9088)
__attribute__((section(".lodict")))
#include "core.fth.h"
static bool exitLpm;
static Addr isr_list[24] = {}; static Addr isr_list[24] = {};
static SplitMemDictRW<sizeof(alee_dat), 16384> dict (alee_dat, 0x10000);
using DictType = SplitMemDictRW<ALEE_RODICTSIZE, 32767>;
extern char __dict[sizeof(DictType)];
static auto& dict = *(new (__dict) DictType (alee_dat, 0x10000));
int main() int main()
{ {
WDTCTL = WDTPW | WDTHOLD; WDTCTL = WDTPW | WDTHOLD;
extern char __libaleebegin;
extern char __libaleeend;
extern char __libaleedst;
std::copy(&__libaleebegin, &__libaleeend, &__libaleedst);
initGPIO(); initGPIO();
initClock(); initClock();
initUART(); initUART();
SYSCFG0 = FRWPPW; SYSCFG0 = FRWPPW;
alee_main();
}
LIBALEE_SECTION
void alee_main()
{
(void)alee_dat_len; (void)alee_dat_len;
State state (dict, readchar); State state (dict, readchar);
Parser::customParse = findword; Parser::customParse = findword;
@ -118,7 +104,6 @@ void alee_main()
} }
} }
LIBALEE_SECTION
void readchar(State& state) void readchar(State& state)
{ {
auto idx = state.dict.read(Dictionary::Input); auto idx = state.dict.read(Dictionary::Input);
@ -131,25 +116,20 @@ void readchar(State& state)
state.dict.writebyte(addr, c ? c : ' '); state.dict.writebyte(addr, c ? c : ' ');
} }
LIBALEE_SECTION
void serput(int c) void serput(int c)
{ {
while (!(UCA0IFG & UCTXIFG)); while (!(UCA0IFG & UCTXIFG));
UCA0TXBUF = static_cast<char>(c); UCA0TXBUF = static_cast<char>(c);
} }
LIBALEE_SECTION
void serputs(const char *s) void serputs(const char *s)
{ {
while (*s) while (*s)
serput(*s++); serput(*s++);
} }
LIBALEE_SECTION void printint(DoubleCell n, char *buf)
void printint(DoubleCell n, char *buf, int base)
{ {
static const char digit[] = "0123456789ABCDEF";
char *ptr = buf; char *ptr = buf;
bool neg = n < 0; bool neg = n < 0;
@ -157,8 +137,8 @@ void printint(DoubleCell n, char *buf, int base)
n = -n; n = -n;
do { do {
*ptr++ = digit[n % base]; *ptr++ = static_cast<char>((n % 10) + '0');
} while ((n /= base)); } while ((n /= 10));
if (neg) if (neg)
serput('-'); serput('-');
@ -169,12 +149,11 @@ void printint(DoubleCell n, char *buf, int base)
serput(' '); serput(' ');
} }
LIBALEE_SECTION
void user_sys(State& state) void user_sys(State& state)
{ {
switch (state.pop()) { switch (state.pop()) {
case 0: // . case 0: // .
printint(state.pop(), strbuf, state.dict.read(Dictionary::Base)); printint(state.pop(), strbuf);
break; break;
case 1: // unused case 1: // unused
state.push(static_cast<Addr>(state.dict.capacity() - state.dict.here())); state.push(static_cast<Addr>(state.dict.capacity() - state.dict.here()));
@ -206,14 +185,6 @@ void user_sys(State& state)
case 16: case 16:
_bic_SR_register(state.pop()); _bic_SR_register(state.pop());
break; break;
case 17:
exitLpm |= true;
break;
case 50:
Parser::customParse = nullptr;
extern char _etext;
state.push((Addr)&_etext);
break;
default: default:
break; break;
} }
@ -221,14 +192,14 @@ void user_sys(State& state)
#define LZSS_MAGIC_SEPARATOR (0xFB) #define LZSS_MAGIC_SEPARATOR (0xFB)
static uint8_t lzword[32]; static char lzword[32];
static int lzwlen; static int lzwlen;
static uint8_t lzbuf[32]; static char lzbuf[32];
static uint8_t *lzptr; static char *lzptr;
Error findword(State& state, Word word) Error findword(State& state, Word word)
{ {
uint8_t *ptr = lzword; char *ptr = lzword;
for (auto it = word.begin(&state.dict); it != word.end(&state.dict); ++it) { for (auto it = word.begin(&state.dict); it != word.end(&state.dict); ++it) {
*ptr = *it; *ptr = *it;
if (islower(*ptr)) if (islower(*ptr))
@ -242,9 +213,9 @@ Error findword(State& state, Word word)
auto ret = decode([](int c) { auto ret = decode([](int c) {
if (c != LZSS_MAGIC_SEPARATOR) { if (c != LZSS_MAGIC_SEPARATOR) {
*lzptr++ = (uint8_t)c; *lzptr++ = (char)c;
} else { } else {
if (lzwlen == lzptr - lzbuf - 2 && std::equal(lzbuf, lzptr - 2, lzword)) { if (lzwlen == lzptr - lzbuf - 2 && strncmp(lzword, lzbuf, lzptr - lzbuf - 2) == 0) {
lzwlen = (*(lzptr - 2) << 8) | *(lzptr - 1); lzwlen = (*(lzptr - 2) << 8) | *(lzptr - 1);
return 1; return 1;
} else { } else {
@ -289,47 +260,35 @@ void initGPIO()
void initClock() 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 __bis_SR_register(SCG0); // disable FLL
CSCTL3 |= SELREF__XT1CLK; // Set XT1 as FLL reference source CSCTL3 |= SELREF__REFOCLK; // Set REFO as FLL reference source
CSCTL1 = DCOFTRIMEN_1 | DCOFTRIM0 | DCOFTRIM1 | DCORSEL_5;// DCOFTRIM=5, DCO Range = 16MHz CSCTL1 = DCOFTRIMEN_1 | DCOFTRIM0 | DCOFTRIM1 | DCORSEL_3;// DCOFTRIM=3, DCO Range = 8MHz
CSCTL2 = FLLD_0 + 487; // DCOCLKDIV = 16MHz CSCTL2 = FLLD_0 + 243; // DCODIV = 8MHz
__delay_cycles(3); __delay_cycles(3);
__bic_SR_register(SCG0); // enable FLL __bic_SR_register(SCG0); // enable FLL
Software_Trim(); // Software Trim to get the best DCOFTRIM value Software_Trim(); // Software Trim to get the best DCOFTRIM value
CSCTL4 = SELMS__DCOCLKDIV | SELA__XT1CLK; // set XT1 (~32768Hz) as ACLK source, ACLK = 32768Hz CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK; // set default REFO(~32768Hz) as ACLK source, ACLK = 32768Hz
// default DCOCLKDIV as MCLK and SMCLK source // default DCODIV as MCLK and SMCLK source
} }
void initUART() void initUART()
{ {
// Configure UART pins // Configure UART pins
P5SEL0 |= BIT1 | BIT2; P5SEL0 |= BIT1 | BIT2; // set 2-UART pin as second function
SYSCFG3|=USCIA0RMP; //Set the remapping source SYSCFG3|=USCIA0RMP; //Set the remapping source
// Configure UART
UCA0CTLW0 |= UCSWRST; UCA0CTLW0 |= UCSWRST;
UCA0CTLW0 |= UCSSEL__SMCLK; // 16 MHz UCA0CTLW0 |= UCSSEL__SMCLK;
// Baud Rate calculation // Baud Rate calculation
// N = 16MHz / 115200 = 138.888 // 8000000/(16*9600) = 52.083
// OS16 = 1, UCBRx = INT(N/16) = 8(.6806) // Fractional portion = 0.083
// UCBRFx = INT( ((N/16) - UCBRx) * 16) = 10(.8896) // User's Guide Table 17-4: UCBRSx = 0x49
UCA0BRW = 8; // UCBRFx = int ( (52.083-52)*16) = 1
UCA0MCTLW = 0xD600 | 0x00A0 | UCOS16; UCA0BR0 = 52; // 8000000/16/9600
UCA0BR1 = 0x00;
UCA0MCTLW = 0x4900 | UCOS16 | UCBRF_1;
UCA0CTLW0 &= ~UCSWRST; // Initialize eUSCI UCA0CTLW0 &= ~UCSWRST; // Initialize eUSCI
} }
@ -403,25 +362,19 @@ void Software_Trim()
while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // Poll until FLL is locked while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // Poll until FLL is locked
} }
bool alee_isr_handle(unsigned index) void alee_isr_handle(unsigned index)
{ {
const Addr isr = isr_list[index]; const Addr isr = isr_list[index];
if (isr != 0) { if (isr != 0) {
State isrstate (dict, readchar); State isrstate (dict, readchar);
exitLpm = false;
isrstate.execute(isr); isrstate.execute(isr);
return exitLpm;
} }
return false;
} }
#define DEFINE_ISR(VVV, III) \ #define DEFINE_ISR(VVV, III) \
__attribute__((interrupt(VVV))) \ __attribute__((interrupt(VVV))) \
void VVV##_ISR() { \ void VVV##_ISR() { alee_isr_handle(III); }
if (alee_isr_handle(III)) \
_low_power_mode_off_on_exit(); }
DEFINE_ISR(ECOMP0_VECTOR, 0) DEFINE_ISR(ECOMP0_VECTOR, 0)
DEFINE_ISR(PORT6_VECTOR, 1) DEFINE_ISR(PORT6_VECTOR, 1)
@ -448,6 +401,3 @@ DEFINE_ISR(TIMER1_A0_VECTOR, 21)
DEFINE_ISR(TIMER0_A1_VECTOR, 22) DEFINE_ISR(TIMER0_A1_VECTOR, 22)
DEFINE_ISR(TIMER0_A0_VECTOR, 23) DEFINE_ISR(TIMER0_A0_VECTOR, 23)
// Override newlib's free to save hundreds of bytes
extern "C" void free(void *) {}

@ -1,9 +0,0 @@
#!/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

@ -1,15 +0,0 @@
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! ;

@ -1,13 +0,0 @@
\ 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! ;

@ -4232,32 +4232,32 @@ sfr_b(UCB1IV_H);
* Interrupt Vectors (offset from 0xFF80 + 0x10 for Password) * Interrupt Vectors (offset from 0xFF80 + 0x10 for Password)
************************************************************/ ************************************************************/
#define ECOMP0_VECTOR (0x14) /* 0xFFCA */ #define ECOMP0_VECTOR (20) /* 0xFFCA */
#define PORT6_VECTOR (0x15) /* 0xFFCC */ #define PORT6_VECTOR (21) /* 0xFFCC */
#define PORT5_VECTOR (0x16) /* 0xFFCE */ #define PORT5_VECTOR (22) /* 0xFFCE */
#define PORT4_VECTOR (0x17) /* 0xFFD0 */ #define PORT4_VECTOR (23) /* 0xFFD0 */
#define PORT3_VECTOR (0x18) /* 0xFFD2 */ #define PORT3_VECTOR (24) /* 0xFFD2 */
#define PORT2_VECTOR (0x19) /* 0xFFD4 */ #define PORT2_VECTOR (25) /* 0xFFD4 */
#define PORT1_VECTOR (0x1A) /* 0xFFD6 */ #define PORT1_VECTOR (26) /* 0xFFD6 */
#define ADC_VECTOR (0x1B) /* 0xFFD8 */ #define ADC_VECTOR (27) /* 0xFFD8 */
#define EUSCI_B1_VECTOR (0x1C) /* 0xFFDA */ #define EUSCI_B1_VECTOR (28) /* 0xFFDA */
#define EUSCI_B0_VECTOR (0x1D) /* 0xFFDC */ #define EUSCI_B0_VECTOR (29) /* 0xFFDC */
#define EUSCI_A1_VECTOR (0x1E) /* 0xFFDE */ #define EUSCI_A1_VECTOR (30) /* 0xFFDE */
#define EUSCI_A0_VECTOR (0x1F) /* 0xFFE0 */ #define EUSCI_A0_VECTOR (31) /* 0xFFE0 */
#define WDT_VECTOR (0x20) /* 0xFFE2 */ #define WDT_VECTOR (32) /* 0xFFE2 */
#define RTC_VECTOR (0x21) /* 0xFFE4 */ #define RTC_VECTOR (33) /* 0xFFE4 */
#define TIMER0_B1_VECTOR (0x22) /* 0xFFE6 */ #define TIMER0_B1_VECTOR (34) /* 0xFFE6 */
#define TIMER0_B0_VECTOR (0x23) /* 0xFFE8 */ #define TIMER0_B0_VECTOR (35) /* 0xFFE8 */
#define TIMER3_A1_VECTOR (0x24) /* 0xFFEA */ #define TIMER3_A1_VECTOR (36) /* 0xFFEA */
#define TIMER3_A0_VECTOR (0x25) /* 0xFFEC */ #define TIMER3_A0_VECTOR (37) /* 0xFFEC */
#define TIMER2_A1_VECTOR (0x26) /* 0xFFEE */ #define TIMER2_A1_VECTOR (38) /* 0xFFEE */
#define TIMER2_A0_VECTOR (0x27) /* 0xFFF0 */ #define TIMER2_A0_VECTOR (39) /* 0xFFF0 */
#define TIMER1_A1_VECTOR (0x28) /* 0xFFF2 */ #define TIMER1_A1_VECTOR (40) /* 0xFFF2 */
#define TIMER1_A0_VECTOR (0x29) /* 0xFFF4 */ #define TIMER1_A0_VECTOR (41) /* 0xFFF4 */
#define TIMER0_A1_VECTOR (0x2A) /* 0xFFF6 */ #define TIMER0_A1_VECTOR (42) /* 0xFFF6 */
#define TIMER0_A0_VECTOR (0x2B) /* 0xFFF8 */ #define TIMER0_A0_VECTOR (43) /* 0xFFF8 */
#define UNMI_VECTOR (0x2C) /* 0xFFFA */ #define UNMI_VECTOR (44) /* 0xFFFA */
#define SYSNMI_VECTOR (0x2D) /* 0xFFFC */ #define SYSNMI_VECTOR (45) /* 0xFFFC */
#define RESET_VECTOR ("reset") /* 0xFFFE Reset (Highest Priority) */ #define RESET_VECTOR ("reset") /* 0xFFFE Reset (Highest Priority) */

@ -44,10 +44,11 @@ MEMORY {
BOOTCODE : ORIGIN = 0x1C00, LENGTH = 0x0400 /* END=0x1FFF, size 1024 */ BOOTCODE : ORIGIN = 0x1C00, LENGTH = 0x0400 /* END=0x1FFF, size 1024 */
ROMLIB : ORIGIN = 0xC0000, LENGTH = 0x4000 /* END=0xC3FFF, size 16384 */ ROMLIB : ORIGIN = 0xC0000, LENGTH = 0x4000 /* END=0xC3FFF, size 16384 */
BSL1 : ORIGIN = 0xFFC00, LENGTH = 0x0400 /* END=0xFFFFF, size 1024 */ BSL1 : ORIGIN = 0xFFC00, LENGTH = 0x0400 /* END=0xFFFFF, size 1024 */
RAM (rwx) : ORIGIN = 0x2000, LENGTH = 0x2000 /* END=0x3FFF, size 8192 */ RAM : ORIGIN = 0x2000, LENGTH = 0x2000 /* END=0x3FFF, size 8192 */
INFOMEM : ORIGIN = 0x1800, LENGTH = 0x0200 /* END=0x19FF, size 512 */ INFOMEM : ORIGIN = 0x1800, LENGTH = 0x0200 /* END=0x19FF, size 512 */
FRAM (rwx) : ORIGIN = 0x8000, LENGTH = 0x7F80 /* END=0xFF7F, size 32640 */ FRAM (rx) : ORIGIN = 0x8000, LENGTH = 0x6600 /* END=0xAFFF, size 9216 */
HIFRAM (rxw) : ORIGIN = 0x10000, LENGTH = 0x00007FFF LOFRAM (rxw) : ORIGIN = 0xE600, LENGTH = 0x1980 /* END=0xFF7F, size 23424 */
HIFRAM (rxw) : ORIGIN = 0x00010000, LENGTH = 0x00007FFF
JTAGSIGNATURE : ORIGIN = 0xFF80, LENGTH = 0x0004 JTAGSIGNATURE : ORIGIN = 0xFF80, LENGTH = 0x0004
BSLSIGNATURE : ORIGIN = 0xFF84, LENGTH = 0x0004 BSLSIGNATURE : ORIGIN = 0xFF84, LENGTH = 0x0004
BSLCONFIGURATIONSIGNATURE : ORIGIN = 0xFF88, LENGTH = 0x0002 BSLCONFIGURATIONSIGNATURE : ORIGIN = 0xFF88, LENGTH = 0x0002
@ -163,11 +164,19 @@ SECTIONS
KEEP (*(.resetvec)) KEEP (*(.resetvec))
} > RESETVEC } > RESETVEC
.lower.rodata :
{
. = ALIGN(2);
*(.lower.rodata.* .lower.rodata)
} > FRAM
.rodata : .rodata :
{ {
. = ALIGN(2); . = ALIGN(2);
*(.plt)
*(.rodata .rodata.* .gnu.linkonce.r.* .const .const:*) *(.rodata .rodata.* .gnu.linkonce.r.* .const .const:*)
*(.rodata1) *(.rodata1)
KEEP (*(.gcc_except_table)) *(.gcc_except_table.*)
} > FRAM } > FRAM
/* Note: This is a separate .rodata section for sections which are /* Note: This is a separate .rodata section for sections which are
@ -191,6 +200,8 @@ SECTIONS
KEEP (*(SORT(.fini_array.*))) KEEP (*(SORT(.fini_array.*)))
PROVIDE (__fini_array_end = .); PROVIDE (__fini_array_end = .);
. = ALIGN(2); . = ALIGN(2);
*(.eh_frame_hdr)
KEEP (*(.eh_frame))
/* gcc uses crtbegin.o to find the start of the constructors, so /* gcc uses crtbegin.o to find the start of the constructors, so
we make sure it is first. Because this is a wildcard, it we make sure it is first. Because this is a wildcard, it
@ -214,27 +225,39 @@ SECTIONS
KEEP (*(.dtors)) KEEP (*(.dtors))
} > FRAM } > FRAM
.tinyram : { /* This section contains data that is initialised during load
PROVIDE (__dict = .); but not on application reset. */
} > TINYRAM .persistent :
{
. = ALIGN(2);
PROVIDE (__persistent_start = .);
*(.persistent)
. = ALIGN(2);
PROVIDE (__persistent_end = .);
} > FRAM
.tinyram : {} > TINYRAM
.libalee : { .lower.data :
{
. = ALIGN(2); . = ALIGN(2);
PROVIDE (__libaleedst = .); PROVIDE (__datastart = .);
*(.libalee) *(.lower.data.* .lower.data)
} > RAM AT> FRAM } > RAM AT> FRAM
PROVIDE(__libaleebegin = LOADADDR(.libalee));
PROVIDE (__libaleeend = LOADADDR(.libalee) + SIZEOF(.libalee));
.data : .data :
{ {
. = ALIGN(2); . = ALIGN(2);
PROVIDE (__datastart = .);
KEEP (*(.jcr))
*(.data.rel.ro.local) *(.data.rel.ro*)
*(.dynamic)
*(.data .data.* .gnu.linkonce.d.*) *(.data .data.* .gnu.linkonce.d.*)
KEEP (*(.gnu.linkonce.d.*personality*)) KEEP (*(.gnu.linkonce.d.*personality*))
SORT(CONSTRUCTORS) SORT(CONSTRUCTORS)
*(.data1) *(.data1)
*(.got.plt) *(.got)
/* We want the small data sections together, so single-instruction offsets /* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so can access them all, and initialized data all before uninitialized, so
@ -251,20 +274,27 @@ SECTIONS
/* Note that crt0 assumes this is a multiple of two; all the /* Note that crt0 assumes this is a multiple of two; all the
start/stop symbols are also assumed word-aligned. */ start/stop symbols are also assumed word-aligned. */
PROVIDE(__romdatastart = LOADADDR(.data)); PROVIDE(__romdatastart = LOADADDR(.lower.data));
PROVIDE (__romdatacopysize = SIZEOF(.data)); PROVIDE (__romdatacopysize = SIZEOF(.lower.data) + SIZEOF(.data));
.bss : .lower.bss :
{ {
. = ALIGN(2); . = ALIGN(2);
PROVIDE (__bssstart = .); PROVIDE (__bssstart = .);
*(.lower.bss.* .lower.bss)
} > RAM
.bss :
{
. = ALIGN(2);
*(.dynbss)
*(.sbss .sbss.*) *(.sbss .sbss.*)
*(.bss .bss.* .gnu.linkonce.b.*) *(.bss .bss.* .gnu.linkonce.b.*)
. = ALIGN(2); . = ALIGN(2);
*(COMMON) *(COMMON)
PROVIDE (__bssend = .); PROVIDE (__bssend = .);
} > RAM } > RAM
PROVIDE (__bsssize = SIZEOF(.bss)); PROVIDE (__bsssize = SIZEOF(.lower.bss) + SIZEOF(.bss));
/* This section contains data that is not initialised during load /* This section contains data that is not initialised during load
or application reset. */ or application reset. */
@ -277,12 +307,47 @@ SECTIONS
PROVIDE (__noinit_end = .); PROVIDE (__noinit_end = .);
} > RAM } > 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)) : .stack (ORIGIN (RAM) + LENGTH(RAM)) :
{ {
PROVIDE (__stack = .); PROVIDE (__stack = .);
*(.stack) *(.stack)
} }
.lower.text :
{
. = ALIGN(2);
*(.lower.text.* .lower.text)
} > FRAM
.lodict :
{
. = ALIGN(2);
*(.lodict)
} > LOFRAM
.hidict : .hidict :
{ {
. = ALIGN(2); . = ALIGN(2);
@ -296,22 +361,23 @@ SECTIONS
. = ALIGN(2); . = ALIGN(2);
KEEP (*(SORT(.crt_*))) KEEP (*(SORT(.crt_*)))
. = ALIGN(2);
KEEP (*(.lowtext))
. = ALIGN(2); . = ALIGN(2);
*(.text .stub .text.* .gnu.linkonce.t.* .text:*) *(.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); . = ALIGN(2);
KEEP (*(.init)) KEEP (*(.init))
KEEP (*(.fini)) KEEP (*(.fini))
KEEP (*(.tm_clone_table)) KEEP (*(.tm_clone_table))
. = ALIGN(2);
PROVIDE (_etext = .);
} > FRAM
.lodict :
{
. = ALIGN(1024);
*(.lodict)
} > FRAM } > FRAM
.info (NOLOAD) : {} > INFOMEM /* MSP430 INFO FLASH MEMORY SEGMENTS */ .info (NOLOAD) : {} > INFOMEM /* MSP430 INFO FLASH MEMORY SEGMENTS */
@ -364,7 +430,7 @@ SECTIONS
/* DWARF Extension. */ /* DWARF Extension. */
.debug_macro 0 : { *(.debug_macro) } .debug_macro 0 : { *(.debug_macro) }
/DISCARD/ : { *(.note.GNU-stack .debug_loclists) } /DISCARD/ : { *(.note.GNU-stack) }
} }

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

@ -19,7 +19,7 @@
#ifndef ALEEFORTH_SPLITMEMDICTRW_HPP #ifndef ALEEFORTH_SPLITMEMDICTRW_HPP
#define ALEEFORTH_SPLITMEMDICTRW_HPP #define ALEEFORTH_SPLITMEMDICTRW_HPP
#include "libalee/alee.hpp" #include "alee.hpp"
#include <algorithm> #include <algorithm>
@ -40,6 +40,7 @@ public:
return *this; return *this;
} }
virtual Cell read(Addr addr) const noexcept final { virtual Cell read(Addr addr) const noexcept final {
if (addr < LON) if (addr < LON)
return *reinterpret_cast<const Cell *>(lodict + addr); return *reinterpret_cast<const Cell *>(lodict + addr);
@ -71,9 +72,6 @@ public:
virtual unsigned long int capacity() const noexcept final { virtual unsigned long int capacity() const noexcept final {
return LON + HIN; return LON + HIN;
} }
private:
virtual ~SplitMemDictRW() override {};
}; };
#endif // ALEEFORTH_SPLITMEMDICTRW_HPP #endif // ALEEFORTH_SPLITMEMDICTRW_HPP

Loading…
Cancel
Save