diff --git a/.gitignore b/.gitignore index fb13a21..a877dd4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .* *.o alee +alee-msp430 libalee.a diff --git a/Makefile b/Makefile index 65dc791..45011b6 100644 --- a/Makefile +++ b/Makefile @@ -1,24 +1,32 @@ CXXFLAGS += -std=c++17 -g3 -ggdb -O0 \ - -Wall -Wextra -pedantic -Wno-vla -Werror + -Wall -Wextra -pedantic -Werror \ + -fno-exceptions -fno-rtti #-fstack-usage CXXFILES := corewords.cpp dictionary.cpp parser.cpp state.cpp OBJFILES := $(subst .cpp,.o,$(CXXFILES)) LIBFILE := libalee.a -EXEFILE := alee -all: $(EXEFILE) +all: alee + +msp430: CXX := msp430-elf32-g++ +msp430: AR := msp430-elf32-ar +msp430: CXXFLAGS += -Os -mmcu=msp430g2553 -ffunction-sections -fdata-sections -DMEMDICTSIZE=200 +msp430: LDFLAGS += -L/opt/msp430-elf32/include -Wl,-gc-sections +msp430: alee-msp430 small: CXXFLAGS += -Os -small: $(EXEFILE) +small: alee fast: CXXFLAGS += -O3 -march=native -mtune=native -fast: $(EXEFILE) +fast: alee + +alee: $(LIBFILE) -$(EXEFILE): $(LIBFILE) +alee-msp430: $(LIBFILE) $(LIBFILE): $(OBJFILES) $(AR) cr $@ $(OBJFILES) clean: - rm -f $(EXEFILE) $(LIBFILE) $(OBJFILES) + rm -f alee alee-msp430 $(LIBFILE) $(OBJFILES) diff --git a/alee-msp430.cpp b/alee-msp430.cpp new file mode 100644 index 0000000..00151f0 --- /dev/null +++ b/alee-msp430.cpp @@ -0,0 +1,162 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#include "alee.hpp" +#include "memdict.hpp" + +#include + +static char strbuf[32]; + +static void readchar(State& state); +static void serput(int c); +static void serputs(const char *s); +static void printint(int n, char *buf); + +int main() +{ + WDTCTL = WDTPW | WDTHOLD; + DCOCTL = 0; + BCSCTL1 = CALBC1_1MHZ; + DCOCTL = CALDCO_1MHZ; + + P1SEL |= BIT1 | BIT2; + P1SEL2 |= BIT1 | BIT2; + + UCA0CTL1 = UCSWRST; + UCA0CTL1 |= UCSSEL_2; + UCA0BR0 = 104; + UCA0BR1 = 0; + UCA0MCTL = UCBRS0; + UCA0CTL1 &= (uint8_t)~UCSWRST; + + __enable_interrupt(); + + static MemDict dict; + State state (dict, readchar); + Parser parser; + + dict.write(Dictionary::Base, 10); + dict.write(Dictionary::Latest, Dictionary::Begin); + dict.write(Dictionary::Compiling, 0); + dict.write(Dictionary::Postpone, 0); + + serputs("alee forth\n\r"); + + auto ptr = strbuf; + while (1) { + if (IFG2 & UCA0RXIFG) { + char c = UCA0RXBUF; + serput(c); + + if (c == '\r') { + *ptr = '\0'; + + serputs("\n\r"); + + if (auto r = parser.parse(state, strbuf); r == 0) { + serputs(state.compiling() ? " compiled" : " ok"); + } else { + switch (r) { + case Parser::UnknownWord: + serputs("unknown word..."); + break; + default: + break; + } + } + + serputs("\n\r"); + + ptr = strbuf; + } else if (c == '\b') { + if (ptr > strbuf) + --ptr; + } else if (ptr < strbuf + sizeof(strbuf)) { + if (c >= 'A' && c <= 'Z') + c += 32; + *ptr++ = c; + } + } + + } + + return 0; +} + +static void readchar(State& state) +{ + auto len = state.dict.read(Dictionary::Input); + Addr addr = Dictionary::Input + sizeof(Cell) + + Dictionary::InputCells - len - 1; + + for (Cell i = 0; i < len; ++i, ++addr) + state.dict.writebyte(addr, state.dict.readbyte(addr + 1)); + + while (!(IFG2 & UCA0RXIFG)); + state.dict.writebyte(addr, UCA0RXBUF); + state.dict.write(Dictionary::Input, len + 1); +} + +void serput(int c) +{ + while (!(IFG2 & UCA0TXIFG)); + UCA0TXBUF = (char)c; +} + +void serputs(const char *s) +{ + while (*s) + serput(*s++); +} + +void printint(int n, char *buf) +{ + char *ptr = buf; + bool neg = n < 0; + + if (neg) + n = -n; + + do { + *ptr++ = (char)(n % 10) + '0'; + } while ((n /= 10)); + + if (neg) + serput('-'); + + do { + serput(*--ptr); + } while (ptr > buf); + serput(' '); +} + +void user_sys(State& state) +{ + switch (state.pop()) { + case 0: + printint(state.pop(), strbuf); + break; + case 1: + serput(state.pop()); + break; + default: + break; + } +} + diff --git a/alee.cpp b/alee.cpp index 41610e4..0f270a8 100644 --- a/alee.cpp +++ b/alee.cpp @@ -26,7 +26,7 @@ static bool okay = false; static void readchar(State& state); -static void parseLine(Parser&, State&, std::string_view); +static void parseLine(Parser&, State&, const std::string&); static void parseFile(Parser&, State&, std::istream&); int main(int argc, char *argv[]) @@ -105,9 +105,9 @@ void user_sys(State& state) } } -void parseLine(Parser& parser, State& state, std::string_view line) +void parseLine(Parser& parser, State& state, const std::string& line) { - if (auto r = parser.parse(state, line); r == 0) { + if (auto r = parser.parse(state, line.c_str()); r == 0) { if (okay) std::cout << (state.compiling() ? "compiled" : "ok") << std::endl; } else { @@ -116,6 +116,7 @@ void parseLine(Parser& parser, State& state, std::string_view line) std::cout << "word not found in: " << line << std::endl; break; default: + std::cout << "error: " << r << std::endl; break; } } diff --git a/corewords.cpp b/corewords.cpp index 2d98117..c0cec48 100644 --- a/corewords.cpp +++ b/corewords.cpp @@ -18,6 +18,9 @@ #include "corewords.hpp" +#include +#include + void CoreWords::run(unsigned int index, State& state) { auto getword = [&state] { @@ -197,18 +200,19 @@ void CoreWords::run(unsigned int index, State& state) } } -int CoreWords::findi(std::string_view word) +int CoreWords::findi(const char *word) { + const auto size = std::strlen(word); std::size_t i; int wordsi = 0; - std::string_view words (wordsarr, sizeof(wordsarr)); - - for (i = 0; i < words.size();) { - const auto end = words.find_first_of({"\0\1", 2}, i); + for (i = 0; i < sizeof(wordsarr);) { + auto end = i; + while (wordsarr[end] > '\1') + ++end; - if (word == words.substr(i, end - i)) - return words[end] == '\0' ? wordsi : (wordsi | Compiletime); + if (size == end - i && !std::strncmp(word, wordsarr + i, size)) + return wordsarr[end] == '\0' ? wordsi : (wordsi | Compiletime); ++wordsi; i = end + 1; @@ -222,13 +226,13 @@ int CoreWords::findi(State& state, Word word) std::size_t i; int wordsi = 0; - std::string_view words (wordsarr, sizeof(wordsarr)); - - for (i = 0; i < words.size();) { - const auto end = words.find_first_of({"\0\1", 2}, i); + for (i = 0; i < sizeof(wordsarr);) { + auto end = i; + while (wordsarr[end] > '\1') + ++end; - if (state.dict.equal(word, words.substr(i, end - i))) - return words[end] == '\0' ? wordsi : (wordsi | Compiletime); + if (state.dict.equal(word, wordsarr + i, end - i)) + return wordsarr[end] == '\0' ? wordsi : (wordsi | Compiletime); ++wordsi; i = end + 1; diff --git a/corewords.hpp b/corewords.hpp index 6f982a0..ab34c78 100644 --- a/corewords.hpp +++ b/corewords.hpp @@ -22,8 +22,6 @@ #include "types.hpp" #include "state.hpp" -#include - void user_sys(State&); class CoreWords @@ -34,7 +32,7 @@ public: constexpr static Cell Immediate = (1 << 5); constexpr static Cell Compiletime = (1 << 6); - static int findi(std::string_view); + static int findi(const char *); static int findi(State&, Word); static void run(unsigned int, State&); diff --git a/dictionary.cpp b/dictionary.cpp index cf04cb4..cb2aba8 100644 --- a/dictionary.cpp +++ b/dictionary.cpp @@ -19,15 +19,16 @@ #include "dictionary.hpp" #include +#include -Addr Dictionary::allot(Cell amount) +Addr Dictionary::allot(Cell amount) noexcept { Addr old = here; here += amount; return old; } -void Dictionary::add(Cell value) +void Dictionary::add(Cell value) noexcept { write(allot(sizeof(Cell)), value); } @@ -41,13 +42,13 @@ Addr Dictionary::aligned(Addr addr) const noexcept return addr; } -Addr Dictionary::alignhere() +Addr Dictionary::alignhere() noexcept { here = aligned(here); return here; } -void Dictionary::addDefinition(Word word) +void Dictionary::addDefinition(Word word) noexcept { add(word.size()); for (auto w = word.start; w != word.end; ++w) @@ -56,7 +57,7 @@ void Dictionary::addDefinition(Word word) alignhere(); } -Addr Dictionary::find(Word word) +Addr Dictionary::find(Word word) noexcept { Addr lt = latest(), oldlt; do { @@ -77,13 +78,13 @@ Addr Dictionary::find(Word word) return 0; } -Addr Dictionary::getexec(Addr addr) +Addr Dictionary::getexec(Addr addr) noexcept { const auto len = read(addr) & 0x1F; return aligned(addr + sizeof(Cell) + len); } -Word Dictionary::input() +Word Dictionary::input() noexcept { auto len = read(Dictionary::Input); if (len != 0) { @@ -111,22 +112,22 @@ Word Dictionary::input() return {}; } -bool Dictionary::equal(Word word, std::string_view sv) const +bool Dictionary::equal(Word word, const char *str, unsigned len) const noexcept { - if (word.size() != sv.size()) + if (word.size() != len) return false; for (auto w = word.start; w != word.end; ++w) { - if (readbyte(w) != sv.front()) + if (readbyte(w) != *str) return false; - sv = sv.substr(1); + ++str; } return true; } -bool Dictionary::equal(Word word, Word other) const +bool Dictionary::equal(Word word, Word other) const noexcept { if (word.size() != other.size()) return false; diff --git a/dictionary.hpp b/dictionary.hpp index fa081cb..9987e8f 100644 --- a/dictionary.hpp +++ b/dictionary.hpp @@ -23,7 +23,6 @@ #include #include -#include class Dictionary { @@ -38,26 +37,26 @@ public: Addr here = Begin; - Addr latest() { return read(Latest); } - void latest(Addr l) { write(Latest, l); } + Addr latest() const noexcept { return read(Latest); } + void latest(Addr l) noexcept { write(Latest, l); } - virtual Cell read(Addr) const = 0; - virtual void write(Addr, Cell) = 0; - virtual uint8_t readbyte(Addr) const = 0; - virtual void writebyte(Addr, uint8_t) = 0; + virtual Cell read(Addr) const noexcept = 0; + virtual void write(Addr, Cell) noexcept = 0; + virtual uint8_t readbyte(Addr) const noexcept = 0; + virtual void writebyte(Addr, uint8_t) noexcept = 0; - Addr alignhere(); + Addr alignhere() noexcept; Addr aligned(Addr) const noexcept; - Addr allot(Cell); - void add(Cell); - void addDefinition(Word); + Addr allot(Cell) noexcept; + void add(Cell) noexcept; + void addDefinition(Word) noexcept; - Addr find(Word); - Addr getexec(Addr); - Word input(); + Addr find(Word) noexcept; + Addr getexec(Addr) noexcept; + Word input() noexcept; - bool equal(Word, std::string_view) const; - bool equal(Word, Word) const; + bool equal(Word, const char *, unsigned) const noexcept; + bool equal(Word, Word) const noexcept; }; #endif // ALEEFORTH_DICTIONARY_HPP diff --git a/memdict.hpp b/memdict.hpp index b956d5d..65063e0 100644 --- a/memdict.hpp +++ b/memdict.hpp @@ -21,26 +21,29 @@ #include "dictionary.hpp" -constexpr unsigned long int MemDictSize = 4096; +#ifndef MEMDICTSIZE +#define MEMDICTSIZE (4096) +#endif +constexpr unsigned long int MemDictSize = MEMDICTSIZE; class MemDict : public Dictionary { uint8_t dict[MemDictSize]; public: - virtual Cell read(Addr addr) const final { + virtual Cell read(Addr addr) const noexcept final { return *reinterpret_cast(dict + addr); } - virtual void write(Addr addr, Cell value) final { + virtual void write(Addr addr, Cell value) noexcept final { *reinterpret_cast(dict + addr) = value; } - virtual uint8_t readbyte(Addr addr) const final { + virtual uint8_t readbyte(Addr addr) const noexcept final { return dict[addr]; } - virtual void writebyte(Addr addr, uint8_t value) final { + virtual void writebyte(Addr addr, uint8_t value) noexcept final { dict[addr] = value; } }; diff --git a/parser.cpp b/parser.cpp index 01dbb90..af5da69 100644 --- a/parser.cpp +++ b/parser.cpp @@ -19,17 +19,19 @@ #include "corewords.hpp" #include "parser.hpp" -#include -#include +#include +#include -int Parser::parse(State& state, std::string_view& str) +int Parser::parse(State& state, const char *str) { + const auto size = std::strlen(str); + auto addr = Dictionary::Input; - state.dict.write(addr, str.size() + 1); + state.dict.write(addr, size + 1); - addr += sizeof(Cell) + Dictionary::InputCells - str.size() - 1; - for (char c : str) - state.dict.writebyte(addr++, c); + addr += sizeof(Cell) + Dictionary::InputCells - size - 1; + while (*str) + state.dict.writebyte(addr++, *str++); state.dict.writebyte(addr, ' '); return parseSource(state); @@ -74,7 +76,8 @@ int Parser::parseWord(State& state, Word word) } else if (state.compiling() && !imm) { state.dict.add(ins); } else { - state.execute(ins); + if (auto stat = state.execute(ins); stat != State::Error::none) + return static_cast(stat); } return 0; @@ -88,11 +91,11 @@ int Parser::parseNumber(State& state, Word word) buf[i] = state.dict.readbyte(word.start + i); buf[i] = '\0'; - char *p; - const auto base = state.dict.read(0); - const Cell l = std::strtol(buf, &p, base); + auto base = state.dict.read(0); + Cell l; + auto [ptr, ec] = std::from_chars(buf, buf + i, l, base); - if (static_cast(std::distance(buf, p)) == word.size()) { + if (ec == std::errc() && ptr == buf + i) { if (state.compiling()) { state.dict.add(CoreWords::findi("_lit")); state.dict.add(l); diff --git a/parser.hpp b/parser.hpp index 8a322e2..5bcf3f9 100644 --- a/parser.hpp +++ b/parser.hpp @@ -28,7 +28,7 @@ class Parser public: constexpr static int UnknownWord = -1; - int parse(State&, std::string_view&); + int parse(State&, const char *); private: int parseSource(State&); diff --git a/state.cpp b/state.cpp index c371101..1510352 100644 --- a/state.cpp +++ b/state.cpp @@ -31,19 +31,26 @@ void State::compiling(bool yes) dict.write(Dictionary::Compiling, yes); } -void State::execute(Addr addr) +State::Error State::execute(Addr addr) { - if (addr < CoreWords::WordCount) { - CoreWords::run(addr, *this); - } else { - pushr(0); - ip = addr - sizeof(Cell); + auto stat = setjmp(jmpbuf); + if (!stat) { + if (addr < CoreWords::WordCount) { + CoreWords::run(addr, *this); + } else { + pushr(0); + ip = addr - sizeof(Cell); - do { - ip += sizeof(Cell); - CoreWords::run(dict.read(ip), *this); - } while (ip); + do { + ip += sizeof(Cell); + CoreWords::run(dict.read(ip), *this); + } while (ip); + } + } else { + return static_cast(stat); } + + return State::Error::none; } std::size_t State::size() const noexcept diff --git a/state.hpp b/state.hpp index 279aefd..22f0d74 100644 --- a/state.hpp +++ b/state.hpp @@ -22,6 +22,7 @@ #include "dictionary.hpp" #include "types.hpp" +#include #include constexpr unsigned DataStackSize = 8; @@ -29,6 +30,16 @@ constexpr unsigned ReturnStackSize = 8; struct State { + enum class Error : int { + none, + push, + pop, + pushr, + popr, + top, + pick + }; + Addr ip = 0; Dictionary& dict; void (*input)(State&); @@ -38,26 +49,28 @@ struct State Cell *dsp = dstack - 1; Cell *rsp = rstack - 1; + std::jmp_buf jmpbuf = {}; + constexpr State(Dictionary& d, void (*i)(State&)): dict(d), input(i) {} bool compiling() const; void compiling(bool); - void execute(Addr); + Error execute(Addr); std::size_t size() const noexcept; std::size_t rsize() const noexcept; inline void push(Cell value) { if (dsp == dstack + DataStackSize - 1) - throw exc_push(); + std::longjmp(jmpbuf, static_cast(Error::push)); *++dsp = value; } inline Cell pop() { if (dsp < dstack) - throw exc_pop(); + std::longjmp(jmpbuf, static_cast(Error::pop)); return *dsp--; } @@ -68,34 +81,27 @@ struct State inline void pushr(Cell value) { if (rsp == rstack + ReturnStackSize - 1) - throw exc_pushr(); + std::longjmp(jmpbuf, static_cast(Error::pushr)); *++rsp = value; } inline Cell popr() { if (rsp < rstack) - throw exc_popr(); + std::longjmp(jmpbuf, static_cast(Error::popr)); return *rsp--; } inline Cell& top() { if (dsp < dstack) - throw exc_top(); + std::longjmp(jmpbuf, static_cast(Error::top)); return *dsp; } inline Cell& pick(std::size_t i) { if (dsp - i < dstack) - throw exc_pick(); + std::longjmp(jmpbuf, static_cast(Error::pick)); return *(dsp - i); } - - struct exc_pop {}; - struct exc_push {}; - struct exc_popr {}; - struct exc_pushr {}; - struct exc_top {}; - struct exc_pick {}; }; #endif // ALEEFORTH_STATE_HPP