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

@ -16,16 +16,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "libalee/alee.hpp"
#include "alee.hpp"
#include "splitmemdict.hpp"
#include <array>
#include <charconv>
#include <chrono>
#include <fstream>
#include <iostream>
#include <vector>
#define ALEE_RODICTSIZE
alignas(sizeof(Cell))
#include "core.fth.h"
static bool okay = false;
@ -84,6 +84,8 @@ static void load(State& state)
void user_sys(State& state)
{
static bool start = false;
static decltype(std::chrono::high_resolution_clock::now()) last;
char buf[32] = {0};
switch (state.pop()) {
@ -104,6 +106,20 @@ void user_sys(State& state)
case 4: // load
load(state);
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:
break;
}

@ -16,21 +16,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "libalee/alee.hpp"
#include "alee.hpp"
#include "memdict.hpp"
#include <charconv>
#include <chrono>
#include <fstream>
#include <iostream>
#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 void readchar(State&);
@ -41,9 +35,6 @@ int main(int argc, char *argv[])
{
MemDict dict;
State state (dict, readchar);
#ifdef ALEE_MSP430
Parser::customParse = findword;
#endif // ALEE_MSP430
dict.initialize();
@ -92,6 +83,8 @@ static void load(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};
switch (state.pop()) {
@ -112,6 +105,20 @@ void user_sys(State& state)
case 4: // load
load(state);
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:
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. ;
( 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
key? if key else dup then 2dup <> while

@ -6,8 +6,6 @@
: cell+ 2 + ;
: cells 2 * ;
: char+ 1 + ;
: chars ;
: . 0 sys ;
: emit 2 sys ;
@ -15,48 +13,50 @@
: 1+ 1 + ;
: 1- 1 - ;
: over 1 pick ;
: rot >r swap r> swap ;
: -rot rot rot ;
: ' _' drop ;
: ! 1 _! ;
: @ 1 _@ ;
: +! dup >r swap r> @ + swap ! ;
: base 0 ;
: here 1 cells @ ;
: allot 1 cells +! ;
: c! 0 _! ;
: c@ 0 _@ ;
: c, here c! 1 allot ;
: char+ 1+ ;
: chars ;
: _latest 2 cells ;
: imm _latest @ dup @ 1 5 << | swap ! ;
: 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
: ] 1 3 cells ! ;
: , here ! 1 cells allot ;
: , 1 cells dup >r @ ! r> dup +! ;
: [ 0 state ! ; imm
: ] 1 state ! ;
: literal [ ' _lit dup , , ] , , ; imm
: ['] ' [ ' literal , ] ; imm
: base 0 ;
: here [ 1 cells ] literal @ ;
: allot [ 1 cells ] literal +! ;
: state [ 3 cells ] literal ;
: _compxt [ 4 cells ] literal ;
: _source [ 5 cells ] literal ;
: _sourceu [ 6 cells ] literal ;
: >in [ 7 cells ] literal ;
: _begin [ 8 cells 80 chars + ] literal ;
: c! 0 _! ;
: c@ 0 _@ ;
: c, here c! 1 allot ;
: if ['] _jmp0 , here 0 , ; imm
: then here swap ! ; imm
: else ['] _jmp , here 0 , swap here swap ! ; imm
: postpone _' dup 0 = if exit then
1 = swap ['] _lit , , if ['] execute ,
else ['] , , then ; imm
1 = swap ['] _lit , , if ['] execute
else ['] , then , ; imm
: over 1 pick ;
: rot >r swap r> swap ;
: -rot rot rot ;
: 2drop drop drop ;
: 2dup over over ;
@ -99,8 +99,7 @@
: j postpone 2r> ['] r> , postpone r@ ['] swap ,
['] >r , ['] -rot , postpone 2>r ; imm
: aligned dup [ 1 cells 1- ] literal swap over & if [ 1 cells ] literal
swap - + else drop then ;
: aligned dup 1 cells 1- swap over & if 1 cells swap - + else drop then ;
: align here dup aligned swap - allot ;
: and & ;
@ -111,7 +110,7 @@
: invert -1 ^ ;
: mod % ;
: 2* 2 * ;
: _msb [ 1 1 cells 8 * 1- << ] literal ;
: _msb 1 1 cells 8 * 1- << ;
: 2/ dup 1 >> swap 0< if _msb or then ;
: /mod 2dup % -rot / ;
@ -146,14 +145,12 @@
2dup <> while rot repeat
2drop here - here c! here ;
: count dup char+ swap c@ ;
: char 0 here char+ c! bl word char+ c@ ;
: char bl word char+ c@ ;
: [char] char postpone literal ; imm
: ( begin [char] ) key <> while repeat ; imm
: _type >r begin dup 0 > while
swap dup c@ r@ execute char+ swap 1- repeat 2drop r> drop ;
: type [ ' emit ] literal _type ;
: type begin dup 0 > while swap dup c@ emit char+ swap 1- repeat 2drop ;
: s" state @ if ['] _jmp , here 0 , then
[char] " word count
state @ 0= if exit then
@ -164,17 +161,17 @@
: :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+ @ ;
: _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 ! ;
: does> state @ if
['] _lit , here 2 cells + , ['] _does> , ['] exit , else
here dup _does> dup _compxt ! 0 , ] then ; imm
: variable create [ 1 cells ] literal allot ;
: variable create 1 cells allot ;
: constant create , does> @ ;
: quit begin _rdepth 1 > while r> drop repeat postpone [ ;
@ -200,7 +197,7 @@
: accept over >r begin dup 0 > while
key dup 32 < if 2drop 0
else dup emit rot 2dup c! char+ swap drop swap 1- then
repeat drop r> - [ 1 chars ] literal / ;
repeat drop r> - 1 chars / ;
: evaluate _source @ >r _sourceu @ >r >in @ >r
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 ;
: byte! 11 sys ;
: byte@ 12 sys ;
: reg! 13 sys ;
: reg@ 14 sys ;
: reg! 11 sys ;
: reg@ 12 sys ;
: 2reg! 13 sys ;
: 2reg@ 14 sys ;
: sr+ 15 sys ;
: sr- 16 sys ;
: lpm-exit 17 sys ;
: 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 ;
: clear ( b r reg/byte -- )
: clear ( b r reg/wreg -- )
>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 ;
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/>.
*/
#include "alee.hpp"
#include "corewords.hpp"
#include "parser.hpp"
#include <utility>
static void find(State&, Word);
static DoubleCell popd(State&);
static void pushd(State&, DoubleCell);
void find(State& state, Word word)
{
Cell tok = 0;
Cell imm = 0;
LIBALEE_SECTION
void CoreWords::run(Cell ins, State& state)
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::Semicolon) ? 1 : -1;
}
state.push(tok);
state.push(imm);
}
bool CoreWords::run(Cell ins, State& state)
{
Cell cell;
DoubleCell dcell;
Addr index = ins;
auto& ip = state.ip();
execute:
if (index >= Dictionary::Begin) {
// must be calling a defined subroutine
state.pushr(ip);
ip = index;
return;
} else switch (index) {
case token("_lit"): // Execution semantics of `literal`.
switch (ins) {
case 0: // _lit
state.push(state.beyondip());
break;
case token("drop"):
case 1: // drop
state.pop();
break;
case token("dup"):
case 2: // dup
state.push(state.top());
break;
case token("swap"):
case 3: // swap
std::swap(state.top(), state.pick(1));
break;
case token("pick"):
case 4: // pick
state.push(state.pick(state.pop()));
break;
case token("sys"): // Calls user-defined "system" handler.
case 5: // sys
user_sys(state);
break;
case token("+"):
cell = state.pop();
state.top() += cell;
case 6: // add
{ auto& cell = state.pop(); state.top() += cell; }
break;
case token("-"):
cell = state.pop();
state.top() -= cell;
case 7: // sub
{ auto& cell = state.pop(); state.top() -= cell; }
break;
case token("m*"): // ( n n -- d )
cell = state.pop();
case 8: // mul ( n n -- d )
{ auto& cell = state.pop();
dcell = state.pop() * cell;
pushd(state, dcell);
state.pushd(dcell); }
break;
case token("_/"): // ( d n -- n )
cell = state.pop();
dcell = popd(state);
state.push(static_cast<Cell>(dcell / cell));
case 9: // div ( d n -- n )
{ auto& cell = state.pop();
dcell = state.popd();
state.push(static_cast<Cell>(dcell / cell)); }
break;
case token("_%"): // ( d n -- n )
cell = state.pop();
dcell = popd(state);
state.push(static_cast<Cell>(dcell % cell));
case 10: // mod ( d n -- n )
{ auto& cell = state.pop();
dcell = state.popd();
state.push(static_cast<Cell>(dcell % cell)); }
break;
case token("_@"): // ( addr cell? -- n )
case 11: // peek
if (state.pop())
state.push(state.dict.read(state.pop()));
else
state.push(state.dict.readbyte(state.pop()));
break;
case token("_!"): // ( n addr cell? -- )
cell = state.pop();
case 12: // poke
{ auto& cell = state.pop();
if (auto addr = state.pop(); cell)
state.dict.write(addr, state.pop());
else
state.dict.writebyte(addr, state.pop() & 0xFFu);
state.dict.writebyte(addr, state.pop() & 0xFFu); }
break;
case token(">r"):
case 13: // pushr
state.pushr(state.pop());
break;
case token("r>"):
case 14: // popr
state.push(state.popr());
break;
case token("="):
cell = state.pop();
state.top() = state.top() == cell ? -1 : 0;
case 15: // equal
{ auto& cell = state.pop();
state.top() = state.top() == cell ? -1 : 0; }
break;
case token("<"):
cell = state.pop();
state.top() = state.top() < cell ? -1 : 0;
case 16: // lt
{ auto& cell = state.pop();
state.top() = state.top() < cell ? -1 : 0; }
break;
case token("&"):
cell = state.pop();
state.top() &= cell;
case 17: // and
{ auto& cell = state.pop(); state.top() &= cell; }
break;
case token("|"):
cell = state.pop();
state.top() |= cell;
case 18: // or
{ auto& cell = state.pop(); state.top() |= cell; }
break;
case token("^"):
cell = state.pop();
state.top() ^= cell;
case 19: // xor
{ auto& cell = state.pop(); state.top() ^= cell; }
break;
case token("<<"):
cell = state.pop();
reinterpret_cast<Addr&>(state.top()) <<= static_cast<Addr>(cell);
case 20: // shl
{ auto& cell = state.pop();
reinterpret_cast<Addr&>(state.top()) <<= static_cast<Addr>(cell); }
break;
case token(">>"):
cell = state.pop();
reinterpret_cast<Addr&>(state.top()) >>= static_cast<Addr>(cell);
case 21: // shr
{ auto& cell = state.pop();
reinterpret_cast<Addr&>(state.top()) >>= static_cast<Addr>(cell); }
break;
case token(":"): // Begins definition/compilation of new word.
case 22: // colon
state.push(state.dict.alignhere());
state.dict.write(Dictionary::CompToken, state.top());
while (!state.dict.hasInput())
@ -136,53 +134,54 @@ execute:
state.dict.addDefinition(state.dict.input());
state.compiling(true);
break;
case token("_'"): // Collects input word and finds execution token.
case 23: // tick
while (!state.dict.hasInput())
state.input();
find(state, state.dict.input());
break;
case token("execute"):
index = state.pop();
goto execute;
case token("exit"):
case 24: // execute
return true;
case 25: // exit
ip = state.popr();
state.verify(ip != 0, Error::exit);
if (ip == 0)
state.exit();
break;
case token(";"): // Concludes word definition.
state.dict.add(token("exit"));
case 26: // semic
state.dict.add(findi("exit"));
state.compiling(false);
cell = state.pop();
{
auto& cell = state.pop();
dcell = cell - state.dict.latest();
if (dcell >= Dictionary::MaxDistance) {
// Large distance to previous entry: store in dedicated cell.
if (dcell > (1 << (sizeof(Cell) * 8 - 6)) - 1) {
state.dict.write(static_cast<Addr>(cell) + sizeof(Cell),
static_cast<Cell>(dcell));
dcell = Dictionary::MaxDistance;
dcell = ((1 << (sizeof(Cell) * 8 - 6)) - 1);
}
state.dict.write(cell,
(state.dict.read(cell) & 0x1F) | static_cast<Cell>(dcell << 6));
state.dict.latest(cell);
}
break;
case token("_jmp0"): // Jump if popped value equals zero.
case 27: // _jmp0
if (state.pop()) {
state.beyondip();
break;
}
[[fallthrough]];
case token("_jmp"): // Unconditional jump.
ip = state.beyondip();
return;
case token("depth"):
case 28: // _jmp
ip = static_cast<Addr>(state.beyondip() - sizeof(Cell));
break;
case 29: // depth
state.push(static_cast<Cell>(state.size()));
break;
case token("_rdepth"):
case 30: // _rdepth
state.push(static_cast<Cell>(state.rsize()));
break;
case token("_in"): // Fetches more input from the user input source.
case 31: // _in
state.input();
break;
case token("_ev"): // Evaluates words from current input source.
case 32: // _ev
{
const auto st = state.save();
ip = 0;
@ -190,30 +189,31 @@ execute:
state.load(st);
}
break;
case token("find"):
cell = state.pop();
case 33: // find
{ auto& cell = state.pop();
find(state,
Word::fromLength(static_cast<Addr>(cell + 1),
state.dict.readbyte(cell)));
state.dict.readbyte(cell))); }
break;
case token("_uma"): // ( d u u -- d ): Unsigned multiply-add.
case 34: // _uma
{
const auto plus = state.pop();
cell = state.pop();
dcell = popd(state);
const auto& plus = state.pop();
const auto& cell = state.pop();
dcell = state.popd();
dcell *= static_cast<Addr>(cell);
dcell += static_cast<Addr>(plus);
pushd(state, dcell);
state.pushd(dcell);
}
break;
case token("u<"):
cell = state.pop();
case 35: // u<
{ auto& cell = state.pop();
state.top() = static_cast<Addr>(state.top()) <
static_cast<Addr>(cell) ? -1 : 0;
static_cast<Addr>(cell) ? -1 : 0; }
break;
case token("um/mod"):
cell = state.pop();
dcell = popd(state);
case 36: // um/mod
{
const auto& cell = state.pop();
dcell = state.popd();
state.push(static_cast<Cell>(
static_cast<DoubleAddr>(dcell) %
@ -221,49 +221,15 @@ execute:
state.push(static_cast<Cell>(
static_cast<DoubleAddr>(dcell) /
static_cast<Addr>(cell)));
break;
default: // Compacted literals (WordCount <= ins < Begin).
state.push(ins - WordCount);
}
break;
}
ip += sizeof(Cell);
return false;
}
LIBALEE_SECTION
Cell CoreWords::findi(State& state, Word word)
{
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
/// @brief Manages the fundamental word-set and its execution.
//
// Alee Forth: A portable and concise Forth implementation in modern C++.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/**
* Alee Forth: A portable and concise Forth implementation in modern C++.
* Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef ALEEFORTH_COREWORDS_HPP
#define ALEEFORTH_COREWORDS_HPP
#include "config.hpp"
#include "dictionary.hpp"
#include "types.hpp"
#include "state.hpp"
#include <algorithm>
class State;
#include <cstring>
/**
* To be implemented by the user, this function is called when the `sys` word
* is executed.
* @param state Current execution state object.
*/
void user_sys(State& state);
void user_sys(State&);
/**
* @class CoreWords
* @brief Provides the fundamental word-set and manages its execution.
*/
class CoreWords
{
public:
/**
* Searches for the token/index of the given word if it is part of the
* fundamental word-set.
* @param state Current execution state object.
* @param word Word (stored in state's dictionary memory) to look up.
* @return The token/index of the word or -1 if not found.
*/
static Cell findi(State& state, Word word);
constexpr static Cell WordCount = 37;
constexpr static Cell Semicolon = 26;
/**
* Looks up the token/index of the given fundamental word.
* Primarily used for compile-time lookup.
* @param word The word to look up.
* @return The token/index of the word or -1 if not found.
* Finds execution token that corresponds to the given word.
* Returns -1 if not found.
*/
consteval static Cell token(const char *word) {
return findi(word, strlen(word));
static Cell findi(State&, Word);
consteval static Cell findi(const char *word) {
return findi(word, std::strlen(word));
}
/**
* Executes the given execution token using the given state.
* @param token Any valid execution token (word, fundamental, constant...).
* @param state The state object to execute with.
* Executes the given CoreWord execution token using the given state.
*/
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[] =
"_lit\0drop\0dup\0swap\0pick\0sys\0"
"+\0-\0m*\0_/\0_%\0"
@ -83,27 +60,14 @@ public:
"depth\0_rdepth\0_in\0_ev\0find\0"
"_uma\0u<\0um/mod\0";
/**
* Count of total fundamental words.
*/
constexpr static Cell WordCount = [] {
return std::count(wordsarr, wordsarr + sizeof(wordsarr), '\0'); }();
private:
/**
* Generic implementation of findi(). Private; use public implementations.
* @param it Beginning iterator of the word to search for.
* @param size Size of the searched-for word i.e. end == it + size.
* @return The token/index of the word or -1 if not found.
*/
template<typename Iter>
LIBALEE_SECTION
constexpr static Cell findi(Iter it, std::size_t size)
{
const char *ptr = CoreWords::wordsarr;
for (Cell wordsi = 0; wordsi < WordCount; ++wordsi) {
std::size_t wordsize = strlen(ptr);
std::size_t wordsize = std::strlen(ptr);
if (wordsize == size && Dictionary::equal(ptr, ptr + wordsize, it))
return wordsi;

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

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

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

@ -16,33 +16,33 @@
* 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;
LIBALEE_SECTION
Error Parser::parse(State& state, const char *str)
{
auto addr = Dictionary::Input;
// Set source and input length
const auto len = static_cast<Cell>(strlen(str));
const auto len = static_cast<Cell>(std::strlen(str));
state.dict.write(addr, 0);
state.dict.write(Dictionary::SourceLen, len);
// Fill input buffer with string contents
addr += sizeof(Cell);
while (*str)
state.dict.writebyte(addr++, *str++);
// Zero the remaining input buffer
while (addr < Dictionary::Input + Dictionary::InputCells)
state.dict.writebyte(addr++, '\0');
return parseSource(state);
}
LIBALEE_SECTION
Error Parser::parseSource(State& state)
{
auto err = Error::none;
@ -53,14 +53,11 @@ Error Parser::parseSource(State& state)
return err;
}
LIBALEE_SECTION
Error Parser::parseWord(State& state, Word word)
{
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) {
auto cw = CoreWords::findi(state, word);
@ -72,7 +69,7 @@ Error Parser::parseWord(State& state, Word word)
return r;
} else {
ins = cw;
imm = ins == CoreWords::token(";");
imm = ins == CoreWords::Semicolon;
}
} else {
imm = state.dict.read(ins) & Dictionary::Immediate;
@ -87,7 +84,6 @@ Error Parser::parseWord(State& state, Word word)
return Error::none;
}
LIBALEE_SECTION
Error Parser::parseNumber(State& state, Word word)
{
const auto base = state.dict.read(Dictionary::Base);
@ -99,7 +95,7 @@ Error Parser::parseNumber(State& state, Word word)
++it;
const auto end = word.end(&state.dict);
for (uint8_t c; it != end; ++it) {
for (char c; it != end; ++it) {
c = *it;
if (isdigit(c)) {
@ -120,15 +116,11 @@ Error Parser::parseNumber(State& state, Word word)
return Error::none;
}
LIBALEE_SECTION
void Parser::processLiteral(State& state, Cell value)
{
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;
if (value >= 0 && value < maxlit)
value += CoreWords::WordCount;

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

@ -16,46 +16,65 @@
* 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>
LIBALEE_SECTION
bool State::compiling() const
{
return dict.read(Dictionary::Compiling);
}
LIBALEE_SECTION
void State::compiling(bool yes)
{
dict.write(Dictionary::Compiling, yes);
}
LIBALEE_SECTION
State::Context State::save()
{
return context;
}
LIBALEE_SECTION
void State::load(const State::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)
{
auto stat = static_cast<Error>(setjmp(context.jmpbuf));
if (stat == Error::none) {
CoreWords::run(addr, *this);
context.ip = 0;
execute1(addr);
if (context.ip >= Dictionary::Begin) {
// longjmp will exit this loop.
for (;;)
CoreWords::run(dict.read(context.ip), *this);
execute1(dict.read(context.ip));
} else {
// addr was a CoreWord, all done now.
context.ip = 0;
@ -67,7 +86,6 @@ Error State::execute(Addr addr)
return stat;
}
LIBALEE_SECTION
void State::reset()
{
while (size())
@ -79,13 +97,11 @@ void State::reset()
context.ip = 0;
}
LIBALEE_SECTION
std::size_t State::size() const noexcept
{
return dsp - dstack;
}
LIBALEE_SECTION
std::size_t State::rsize() const noexcept
{
return rsp - rstack;

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

@ -16,34 +16,30 @@
* 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
{
return wend - start;
}
LIBALEE_SECTION
Word::iterator Word::begin(const Dictionary *dict)
{
return iterator(start, dict);
}
LIBALEE_SECTION
Word::iterator Word::end(const Dictionary *dict)
{
return iterator(wend, dict);
}
LIBALEE_SECTION
Word::iterator& Word::iterator::operator++()
{
addr++;
return *this;
}
LIBALEE_SECTION
Word::iterator Word::iterator::operator++(int)
{
const auto copy = *this;
@ -51,15 +47,12 @@ Word::iterator Word::iterator::operator++(int)
return copy;
}
LIBALEE_SECTION
Word::iterator::value_type Word::iterator::operator*()
{
return dict->readbyte(addr);
}
LIBALEE_SECTION
bool Word::iterator::operator!=(const iterator& other)
{
return dict != other.dict || addr != other.addr;
}

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

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

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

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

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

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

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

Loading…
Cancel
Save