misc/kforth: Filling out more words.
This commit is contained in:
		
							parent
							
								
									6a49675314
								
							
						
					
					
						commit
						7fc402e30a
					
				
							
								
								
									
										3
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										3
									
								
								Makefile
								
								
								
								
							| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
CXXSTD :=	c++14
 | 
					CXXSTD :=	c++14
 | 
				
			||||||
CXXFLAGS :=	-std=$(CXXSTD) -Wall -Werror -Os -static
 | 
					CXXFLAGS :=	-std=$(CXXSTD) -Wall -Werror -O0 -g -static
 | 
				
			||||||
LDFLAGS :=	-static
 | 
					LDFLAGS :=	-static
 | 
				
			||||||
OBJS :=		linux/io.o	\
 | 
					OBJS :=		linux/io.o	\
 | 
				
			||||||
		io.o		\
 | 
							io.o		\
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,6 @@ all: $(TARGET)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(TARGET): $(OBJS)
 | 
					$(TARGET): $(OBJS)
 | 
				
			||||||
	$(CXX) $(CXXFLAGS) -o $@ $(OBJS)
 | 
						$(CXX) $(CXXFLAGS) -o $@ $(OBJS)
 | 
				
			||||||
	strip $@
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
clean:
 | 
					clean:
 | 
				
			||||||
	rm -f $(OBJS) $(TARGET)
 | 
						rm -f $(OBJS) $(TARGET)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					nucleus layer:
 | 
				
			||||||
 | 
					+ !
 | 
				
			||||||
 | 
					+ +!
 | 
				
			||||||
 | 
					+ @
 | 
				
			||||||
 | 
					+ C!
 | 
				
			||||||
 | 
					+ C@
 | 
				
			||||||
 | 
					+ CMOVE
 | 
				
			||||||
 | 
					+ CMOVE>
 | 
				
			||||||
 | 
					+ COUNT
 | 
				
			||||||
 | 
					+ FILL
 | 
				
			||||||
 | 
					+ EXECUTE
 | 
				
			||||||
 | 
					+ EXIT
 | 
				
			||||||
 | 
					+ I
 | 
				
			||||||
 | 
					+ J
 | 
				
			||||||
 | 
					+ >R
 | 
				
			||||||
 | 
					+ R>
 | 
				
			||||||
 | 
					+ R@
 | 
				
			||||||
 | 
					+ U<
 | 
				
			||||||
 | 
					+ UM*
 | 
				
			||||||
 | 
					+ UM/MOD
 | 
				
			||||||
							
								
								
									
										161
									
								
								dict.cc
								
								
								
								
							
							
						
						
									
										161
									
								
								dict.cc
								
								
								
								
							| 
						 | 
					@ -24,8 +24,8 @@ pop_long(System *sys, KF_LONG *d)
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	*d = a << dshift;
 | 
						*d = static_cast<KF_LONG>(a) << dshift;
 | 
				
			||||||
	*d += b;
 | 
						*d += static_cast<KF_LONG>(b);
 | 
				
			||||||
	sys->status = STATUS_OK;
 | 
						sys->status = STATUS_OK;
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1001,6 +1001,22 @@ dlt(System *sys)
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					ddot(System *sys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						KF_LONG	da;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!pop_long(sys, &da)) {
 | 
				
			||||||
 | 
							// Status is already set.
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						write_dnum(sys->interface, da);
 | 
				
			||||||
 | 
						sys->interface->newline();
 | 
				
			||||||
 | 
						sys->status = STATUS_OK;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
negate(System *sys)
 | 
					negate(System *sys)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -1023,20 +1039,161 @@ negate(System *sys)
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					dnegate(System *sys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						KF_LONG	da;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!pop_long(sys, &da)) {
 | 
				
			||||||
 | 
							// Status is already set.
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						da = ~da;
 | 
				
			||||||
 | 
						da++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!push_long(sys, da)) {
 | 
				
			||||||
 | 
							// Status is already set.
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sys->status = STATUS_OK;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					pick(System *sys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						KF_INT	a, b;
 | 
				
			||||||
 | 
						if (!sys->dstack.pop(&a)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t	i = sys->dstack.size() - a - 1;
 | 
				
			||||||
 | 
						if (!sys->dstack.get(i, b)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!sys->dstack.push(b)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_OVERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sys->status = STATUS_OK;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					min(System *sys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						KF_INT	a, b;
 | 
				
			||||||
 | 
						if (!sys->dstack.pop(&a)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!sys->dstack.pop(&b)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!sys->dstack.push(a < b ? a : b)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_OVERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sys->status = STATUS_OK;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					max(System *sys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						KF_INT	a, b;
 | 
				
			||||||
 | 
						if (!sys->dstack.pop(&a)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!sys->dstack.pop(&b)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!sys->dstack.push(a > b ? a : b)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_OVERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sys->status = STATUS_OK;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					exclusive_or(System *sys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						KF_INT	a, b;
 | 
				
			||||||
 | 
						if (!sys->dstack.pop(&a)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!sys->dstack.pop(&b)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!sys->dstack.push(a ^ b)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_OVERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sys->status = STATUS_OK;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					mod(System *sys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						KF_INT	a, b;
 | 
				
			||||||
 | 
						if (!sys->dstack.pop(&a)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!sys->dstack.pop(&b)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!sys->dstack.push(b % a)) {
 | 
				
			||||||
 | 
							sys->status = STATUS_STACK_OVERFLOW;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sys->status = STATUS_OK;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
init_dict(System *sys)
 | 
					init_dict(System *sys)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sys->dict = nullptr;
 | 
						sys->dict = nullptr;
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"SWAP", 4, sys->dict, swap);
 | 
						sys->dict = new Builtin((const char *)"SWAP", 4, sys->dict, swap);
 | 
				
			||||||
 | 
						sys->dict = new Builtin((const char *)"XOR", 3, sys->dict, exclusive_or);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"ROT", 3, sys->dict, rot);
 | 
						sys->dict = new Builtin((const char *)"ROT", 3, sys->dict, rot);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"ROLL", 4, sys->dict, roll);
 | 
						sys->dict = new Builtin((const char *)"ROLL", 4, sys->dict, roll);
 | 
				
			||||||
 | 
						sys->dict = new Builtin((const char *)"PICK", 4, sys->dict, pick);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"OVER", 4, sys->dict, over);
 | 
						sys->dict = new Builtin((const char *)"OVER", 4, sys->dict, over);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"NEGATE", 6, sys->dict, negate);
 | 
						sys->dict = new Builtin((const char *)"NEGATE", 6, sys->dict, negate);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"OR", 2, sys->dict, lor);
 | 
						sys->dict = new Builtin((const char *)"OR", 2, sys->dict, lor);
 | 
				
			||||||
 | 
						sys->dict = new Builtin((const char *)"MOD", 3, sys->dict, mod);
 | 
				
			||||||
 | 
						sys->dict = new Builtin((const char *)"MIN", 3, sys->dict, min);
 | 
				
			||||||
 | 
						sys->dict = new Builtin((const char *)"MAX", 3, sys->dict, max);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"DUP", 3, sys->dict, dup);
 | 
						sys->dict = new Builtin((const char *)"DUP", 3, sys->dict, dup);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"DROP", 4, sys->dict, drop);
 | 
						sys->dict = new Builtin((const char *)"DROP", 4, sys->dict, drop);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"DEPTH", 5, sys->dict, depth);
 | 
						sys->dict = new Builtin((const char *)"DEPTH", 5, sys->dict, depth);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"DEFINITIONS", 11, sys->dict, definitions);
 | 
						sys->dict = new Builtin((const char *)"DEFINITIONS", 11, sys->dict, definitions);
 | 
				
			||||||
 | 
						sys->dict = new Builtin((const char *)"DNEGATE", 7, sys->dict, dnegate);
 | 
				
			||||||
 | 
						sys->dict = new Builtin((const char *)"D.", 2, sys->dict, ddot);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"D<", 2, sys->dict, dlt);
 | 
						sys->dict = new Builtin((const char *)"D<", 2, sys->dict, dlt);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"D+", 2, sys->dict, dplus);
 | 
						sys->dict = new Builtin((const char *)"D+", 2, sys->dict, dplus);
 | 
				
			||||||
	sys->dict = new Builtin((const char *)"BYE", 3, sys->dict, bye);
 | 
						sys->dict = new Builtin((const char *)"BYE", 3, sys->dict, bye);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,322 @@
 | 
				
			||||||
 | 
					Write You a Forth, 0x06
 | 
				
			||||||
 | 
					-----------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:date: 2018-02-28 22:55
 | 
				
			||||||
 | 
					:tags: wyaf, forth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Lots of updates last night; SLOC-wise, I added a bunch of new definitions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ ``DEPTH``, ``.`` and ``.S`` to inspect the stack
 | 
				
			||||||
 | 
					+ ``/MOD``, ``*/``, and ``*/MOD``, which required adding some idea of a long
 | 
				
			||||||
 | 
					  type
 | 
				
			||||||
 | 
					+  ``0<``, ``0=``, ``0>``, ``<``, ``=``, and ``>`` for conditionals
 | 
				
			||||||
 | 
					+ ``DUP`` and ``?DUP``
 | 
				
			||||||
 | 
					+ the logical operators ``AND``, ``OR``, and ``NEGATE``
 | 
				
			||||||
 | 
					+ ``ABS``
 | 
				
			||||||
 | 
					+ ``BYE`` moved from an interpreter hack to a defined word
 | 
				
			||||||
 | 
					+ ``D+`` and ``D-`` started me off on the concept of double numbers
 | 
				
			||||||
 | 
					+ ``DROP``, ``OVER``, and ``ROLL`` are more stack manipulation functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It's starting to feel a lot like a Forth...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Speaking of SLOC, for shits and grins I decided to see how the code base has
 | 
				
			||||||
 | 
					grown:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+-----------+---------------+--------+----------------------+---------------+
 | 
				
			||||||
 | 
					| revision  | lines of code | growth |         focus        | exec size (b) |
 | 
				
			||||||
 | 
					+-----------+---------------+--------+----------------------+---------------+
 | 
				
			||||||
 | 
					| 0x02      |      133      |   n/a  | starting point       |     38368     |
 | 
				
			||||||
 | 
					+-----------+---------------+--------+----------------------+---------------+
 | 
				
			||||||
 | 
					| 0x03      |      245      |   1.8x | parsing              |     40920     |
 | 
				
			||||||
 | 
					+-----------+---------------+--------+----------------------+---------------+
 | 
				
			||||||
 | 
					| 0x04      |      369      |   1.5x | stack / numerics     |     48736     |
 | 
				
			||||||
 | 
					+-----------+---------------+--------+----------------------+---------------+
 | 
				
			||||||
 | 
					| 0x05      |      677      |   1.8x | initial dictionary   |     62896     |
 | 
				
			||||||
 | 
					+-----------+---------------+--------+----------------------+---------------+
 | 
				
			||||||
 | 
					| 0x06      |     1436      |   2.1x | expanding vocabulary |     85256     |
 | 
				
			||||||
 | 
					+-----------+---------------+--------+----------------------+---------------+
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that the executable is compiled with ``-O0 -g`` on the
 | 
				
			||||||
 | 
					``x86_64-linux-gnu`` target.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It makes sense that expanding the vocabulary is going to be a huge code
 | 
				
			||||||
 | 
					expansion. I did do more than that; so, I'm not really going to show most of
 | 
				
			||||||
 | 
					the work I did for the new words (a lot of it is repetative and mechanical).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					System updates
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Before I started expanding the dictionary, though, I made some changes to
 | 
				
			||||||
 | 
					the ``System``::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $ git diff HEAD^ system.h 
 | 
				
			||||||
 | 
					        diff --git a/system.h b/system.h
 | 
				
			||||||
 | 
					        index 00f4a34..91aa1fa 100644
 | 
				
			||||||
 | 
					        --- a/system.h
 | 
				
			||||||
 | 
					        +++ b/system.h
 | 
				
			||||||
 | 
					        @@ -5,11 +5,24 @@
 | 
				
			||||||
 | 
					        #include "io.h"
 | 
				
			||||||
 | 
					        #include "stack.h"
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        +typedef enum _SYS_STATUS : uint8_t {
 | 
				
			||||||
 | 
					        +       STATUS_OK = 0,
 | 
				
			||||||
 | 
					        +       STATUS_STACK_OVERFLOW = 1,
 | 
				
			||||||
 | 
					        +       STATUS_STACK_UNDERFLOW = 2,
 | 
				
			||||||
 | 
					        +       STATUS_EXECUTION_FAILURE = 3,
 | 
				
			||||||
 | 
					        +       STATUS_UNKNOWN_WORD = 4
 | 
				
			||||||
 | 
					        +} SYS_STATUS;
 | 
				
			||||||
 | 
					        +
 | 
				
			||||||
 | 
					        +class Word;
 | 
				
			||||||
 | 
					        +
 | 
				
			||||||
 | 
					        typedef struct _System {
 | 
				
			||||||
 | 
					                Stack<KF_INT>    dstack;
 | 
				
			||||||
 | 
					                IO              *interface;
 | 
				
			||||||
 | 
					        -       struct Word     *dict;
 | 
				
			||||||
 | 
					        +       Word            *dict;
 | 
				
			||||||
 | 
					        +       SYS_STATUS       status;
 | 
				
			||||||
 | 
					        } System;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        +void   system_clear_error(System *sys);
 | 
				
			||||||
 | 
					        +void   system_write_status(System *sys);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #endif // __KF_CORE_H__
 | 
				
			||||||
 | 
					        \ No newline at end of file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I've started adding a notion of system state, which I've deliberately kept
 | 
				
			||||||
 | 
					separate from the parser state. The new functions aren't particularly
 | 
				
			||||||
 | 
					interesting; they just write a string to the ``interface`` field so you
 | 
				
			||||||
 | 
					get things like::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $ ./kforth
 | 
				
			||||||
 | 
					        kforth interpreter
 | 
				
			||||||
 | 
					        ? swap
 | 
				
			||||||
 | 
					        stack underflow (error code 2).
 | 
				
			||||||
 | 
					        ? what-word?
 | 
				
			||||||
 | 
					        unknown word (error code 4).
 | 
				
			||||||
 | 
					        ? 2
 | 
				
			||||||
 | 
					        ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that this is separate from the parser errors::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $ ./kforth 
 | 
				
			||||||
 | 
					        kforth interpreter
 | 
				
			||||||
 | 
					        ? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 | 
				
			||||||
 | 
					        ok.
 | 
				
			||||||
 | 
					        parse error: token too long
 | 
				
			||||||
 | 
					        ? unknown word (error code 4).
 | 
				
			||||||
 | 
					        ? 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Though this test does show that the interpreter could be made more robust.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I/O updates
 | 
				
			||||||
 | 
					^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The next thing I did was move the ``write_dstack`` function into ``io.cc``;
 | 
				
			||||||
 | 
					this is needed to implement ``.S``. While I was at it, I decided to make
 | 
				
			||||||
 | 
					``write_num`` finally work well and correctly, and I think I've got the final
 | 
				
			||||||
 | 
					version done::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void
 | 
				
			||||||
 | 
					        write_num(IO *interface, KF_INT n)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                char buf[nbuflen];
 | 
				
			||||||
 | 
					                uint8_t i = nbuflen - 1;
 | 
				
			||||||
 | 
					                memset(buf, 0, nbuflen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (n < 0) {
 | 
				
			||||||
 | 
					                        interface->wrch('-');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I'm still not proud of this hack, but it seems to be the best way to deal with
 | 
				
			||||||
 | 
					this right now::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                else if (n == 0) {
 | 
				
			||||||
 | 
					                        interface->wrch('0');
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (n != 0) {
 | 
				
			||||||
 | 
					                        char x = n % 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This was the magic that finally got it right: negating the digits as they're
 | 
				
			||||||
 | 
					going into the buffer. No more trying to invert the whole number, just each
 | 
				
			||||||
 | 
					digit::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        x = x < 0 ? -x : x;
 | 
				
			||||||
 | 
					                        x += '0';
 | 
				
			||||||
 | 
					                        buf[i--] = x;
 | 
				
			||||||
 | 
					                        n /= 10;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                interface->wrbuf(buf+i, nbuflen - i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					My first pass at this wrote the string forwards, then reversed it. I didn't
 | 
				
			||||||
 | 
					like that; while performance isn't my first concern, it just seemed like a
 | 
				
			||||||
 | 
					fun challenge to get the reversed buffer written correctly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					While I was in the I/O subsystem, I also decided to update the ``IO`` base
 | 
				
			||||||
 | 
					class to include a ``newline`` method; I had a few instances of
 | 
				
			||||||
 | 
					``interface->wrch('\n')`` sprinkled throughout, but that won't necessarily be
 | 
				
			||||||
 | 
					correct elsewhere.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Miscellaneous updates
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I add a new definition to the ``defs.h`` files: a ``KF_LONG`` type to prepare
 | 
				
			||||||
 | 
					for the double numbers mentioned in the next section, and switched to static
 | 
				
			||||||
 | 
					compilation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					New words!
 | 
				
			||||||
 | 
					^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Finally, I started adding the new words in. I'm still trying to figure out a
 | 
				
			||||||
 | 
					good way to handle the address types (I think I'll just introduce a ``KF_ADDR``
 | 
				
			||||||
 | 
					type) so I've punted on those for now.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. _pforth: http://www.softsynth.com/pforth/
 | 
				
			||||||
 | 
					.. _gforth: https://www.gnu.org/software/gforth/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					One of the interesting challenges is dealing with the double numbers. These are
 | 
				
			||||||
 | 
					on the stack as a pair of smaller numbers, e.g. if the double number type is 64
 | 
				
			||||||
 | 
					bits and the standard number type is 32 bits, then you might see something like
 | 
				
			||||||
 | 
					this (via pforth_)::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        0 1 0 1 D+
 | 
				
			||||||
 | 
					        ok
 | 
				
			||||||
 | 
					        Stack<10> 0 2 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So, how to deal with this? There's a ``D.`` word, which I don't have
 | 
				
			||||||
 | 
					implemented yet, that will let me see what pforth_ and gforth_ do::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $ pforth -q
 | 
				
			||||||
 | 
					        Begin AUTO.INIT ------
 | 
				
			||||||
 | 
					        0 1 D. 1 0 D.
 | 
				
			||||||
 | 
					        4294967296 1 
 | 
				
			||||||
 | 
					        ^C
 | 
				
			||||||
 | 
					        $ gforth   
 | 
				
			||||||
 | 
					        Gforth 0.7.2, Copyright (C) 1995-2008 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					        Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
 | 
				
			||||||
 | 
					        Type `bye' to exit
 | 
				
			||||||
 | 
					        0 1 D. 1 0 D. 18446744073709551616 1  ok
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So, it looks like the first number on the stack is the low part, and the second
 | 
				
			||||||
 | 
					is the high part. This is, once again, pretty straightforward: I'll need to
 | 
				
			||||||
 | 
					shift the first number by the appropriate number of bits and then add the
 | 
				
			||||||
 | 
					second number to it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        constexpr size_t dshift = (sizeof(KF_INT) * 8) - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static bool
 | 
				
			||||||
 | 
					        pop_long(System *sys, KF_LONG *d)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                KF_INT	a = 0;
 | 
				
			||||||
 | 
					                KF_INT	b = 0;
 | 
				
			||||||
 | 
					                if (!sys->dstack.pop(&a)) {
 | 
				
			||||||
 | 
					                        sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (!sys->dstack.pop(&b)) {
 | 
				
			||||||
 | 
					                        sys->status = STATUS_STACK_UNDERFLOW;
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                *d = static_cast<KF_LONG>(a) << dshift;
 | 
				
			||||||
 | 
					                *d += static_cast<KF_LONG>(b);
 | 
				
			||||||
 | 
					                sys->status = STATUS_OK;
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This function also shows off the new status work and how that turns out. I've
 | 
				
			||||||
 | 
					kept the exec interface as a boolean to indicate success or failure.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To push the results back onto the stack, I needed to first write a masking
 | 
				
			||||||
 | 
					function to make sure to clear out any lingering bits::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static inline KF_INT
 | 
				
			||||||
 | 
					        mask(size_t bits)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                KF_INT m = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (size_t i = 0; i < bits; i++) {
 | 
				
			||||||
 | 
					                        m += 1 << i;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                return m;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I should probably check `Hacker's Delight <http://hackersdelight.org/>`_ to see
 | 
				
			||||||
 | 
					if there's any tricks for this.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With the mask available, getting a long into a pair of ints requires shifting
 | 
				
			||||||
 | 
					and clearing for the high part and clearing for the low part::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static bool
 | 
				
			||||||
 | 
					        push_long(System *sys, KF_LONG d)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                KF_INT	a = static_cast<KF_INT>((d >> dshift) & mask(dshift));
 | 
				
			||||||
 | 
					                KF_INT	b = static_cast<KF_INT>(d & mask(dshift));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!sys->dstack.push(b)) {
 | 
				
			||||||
 | 
					                        sys->status = STATUS_STACK_OVERFLOW;
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (!sys->dstack.push(a)) {
 | 
				
			||||||
 | 
					                        sys->status = STATUS_STACK_OVERFLOW;
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                sys->status = STATUS_OK;
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					One of the words that interacts with doubles is ``D+``::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static bool
 | 
				
			||||||
 | 
					        dplus(System *sys)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                KF_LONG	da, db;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (!pop_long(sys, &da)) {
 | 
				
			||||||
 | 
					                        // Status is already set.
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (!pop_long(sys, &db)) {
 | 
				
			||||||
 | 
					                        // Status is already set.
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                da += db;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (!push_long(sys, da)) {
 | 
				
			||||||
 | 
					                        // Status is already set.
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Status is already set.
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The only other thing I really did was to add a ``remove`` method to the Stack
 | 
				
			||||||
 | 
					class to support ``ROLL``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Huge diff, but not as much to say about it --- next up, I think I'm going to
 | 
				
			||||||
 | 
					introduce the ``KF_ADDR`` type and start working on some of the address
 | 
				
			||||||
 | 
					interaction stuff. I'll also add more of the double number words, too. The
 | 
				
			||||||
 | 
					words I still have to implement from the `FORTH-83 standard`_ nuclear layer
 | 
				
			||||||
 | 
					are:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ ``!``, ``+!``, ``@``, ``C!``, ``C@``, ``CMOVE``, ``CMOVE>``, ``COUNT``,
 | 
				
			||||||
 | 
					  ``FILL``: memory manipulation words
 | 
				
			||||||
 | 
					+ ``DNEGATE``, ``MAX``, ``MIN``, ``MOD``, ``XOR``: more arithmetic words
 | 
				
			||||||
 | 
					+ ``EXECUTE``, ``EXIT``, ``I``, ``J``, ``PICK``: various words
 | 
				
			||||||
 | 
					+  ``>R``, ``R>``, ``R@``: return stack words
 | 
				
			||||||
 | 
					+ ``U<``, ``UM*``, ``UM/MOD``: unsigned math words
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. _FORTH-83 standard: http://forth.sourceforge.net/standard/fst83/fst83-12.htm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					As before, the snapshot for this update is tagged `part-0x06
 | 
				
			||||||
 | 
					<https://github.com/kisom/kforth/tree/part-0x06>`_.
 | 
				
			||||||
							
								
								
									
										31
									
								
								io.cc
								
								
								
								
							
							
						
						
									
										31
									
								
								io.cc
								
								
								
								
							| 
						 | 
					@ -3,11 +3,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static constexpr size_t	nbuflen = 11;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
write_num(IO *interface, KF_INT n)
 | 
					write_num(IO *interface, KF_INT n)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						static constexpr size_t	nbuflen = 11;
 | 
				
			||||||
	char buf[nbuflen];
 | 
						char buf[nbuflen];
 | 
				
			||||||
	uint8_t i = nbuflen - 1;
 | 
						uint8_t i = nbuflen - 1;
 | 
				
			||||||
	memset(buf, 0, nbuflen);
 | 
						memset(buf, 0, nbuflen);
 | 
				
			||||||
| 
						 | 
					@ -31,6 +30,34 @@ write_num(IO *interface, KF_INT n)
 | 
				
			||||||
	interface->wrbuf(buf+i, nbuflen - i);
 | 
						interface->wrbuf(buf+i, nbuflen - i);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					write_dnum(IO *interface, KF_LONG n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static constexpr size_t	dnbuflen = 21;
 | 
				
			||||||
 | 
						char buf[dnbuflen];
 | 
				
			||||||
 | 
						uint8_t i = dnbuflen - 1;
 | 
				
			||||||
 | 
						memset(buf, 0, dnbuflen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (n < 0) {
 | 
				
			||||||
 | 
							interface->wrch('-');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (n == 0) {
 | 
				
			||||||
 | 
							interface->wrch('0');
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (n != 0) {
 | 
				
			||||||
 | 
							char x = n % 10;
 | 
				
			||||||
 | 
							x = x < 0 ? -x : x;
 | 
				
			||||||
 | 
							x += '0';
 | 
				
			||||||
 | 
							buf[i--] = x;
 | 
				
			||||||
 | 
							n /= 10;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						interface->wrbuf(buf+i, dnbuflen - i);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
write_dstack(IO *interface, Stack<KF_INT> dstack)
 | 
					write_dstack(IO *interface, Stack<KF_INT> dstack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue