barrym95838 wrote:
scotws wrote:
... Do you have some mechanism to native-compile short words (so DROP is INX INX and not a jump or call)? At the moment, deciding which words to native compile in Tali is rather ad hoc ("looks short"). Once stuff is stable, I'll have to figure out something a bit more scientific based on a "acceptable percentage of overhead".
Sorry that I forgot to answer you sooner, Scot. I am writing a Forth for my
65m32 not for speed or utility, but for learning and shaking out my machine instruction set before putting a final seal on it. I know that I'm on to something good, because I always find myself fighting the urge to code high-level words as primitives, due to the almost inevitable conclusion that so many of them wind up being not just faster, but shorter as well.
In my unfinished [sigh] DTC Forth based on Dr. Brad's Camel Forth, NEXT is
one machine instruction, and the following primitives are all
one (or
zero!) machine instructions (plus NEXT):
Code:
+ - 1+ 1- 2* >BODY @ ALIGN ALIGNED AND branch C@ CELL+ CELLS CHAR+ CHARS DROP DUP enter EXECUTE EXIT INVERT NEGATE NIP OR SWAP UNLOOP XOR [ ]
Dozens of my other primitives are only
two or
three machine instructions (plus NEXT), and I can imagine that I would be doing tons of in-lining in an STC system. In fact, (and this may bring some criticism down on me), I'm having trouble justifying spending any more of my limited spare time learning how to program in Forth, because programming in assembler is so much fun, even though I'm still
hand-assembling everything
. I am porting a version of Forth because the act of porting interests me, but I get this nagging feeling that actually programming something non-trivial in Forth will never feel "natural" to me, like programming in BASIC or C or assembler does. I wind up with something awkward like this, because "stackrobatics" don't come naturally to me (this doesn't quite work correctly, and I haven't figured it out yet):
Code:
: line \ plot solid line ( 'x 'y -- )
\ Draw a solid line from x,y to 'x,'y
\ x and y are global variables, and update to 'x,'y
\ . means "delta "
\ ' means "updated value of "
\ ~ means "sign of delta " ... -1 if <0, +1 if >=0
\ | means "absolute value of delta "
y @ - \ calculate .y ( 'x .y )
dup 0< 1 or swap \ ~.y ( 'x ~y .y )
abs dup >r \ |.y ( 'x ~y |y ) ( R: |y )
rot x @ - \ calculate .x ( ~y |y .x )
dup 0< 1 or swap \ ~.x ( ~y |y ~x .x )
abs \ |.x ( ~y |y ~x |x )
dup \ c is slope accum. ( ~y |y ~x |x c )
dup r> + \ k is loop counter ( ~y |y ~x |x c k ) ( R: )
negate 1 swap do \ main loop ( ( ~y |y ~x |x c ) ( R: 1 -k )
plot \ plot (x,y)
i 0< if \ is line finished? ( ( ~y |y ~x |x c )
dup 0< if \ n: c < 0? (
over + >r \ y: ( c += |x ( ~y |y ~x |x ) ( R: 1 'k 'c )
3 pick y \ y += ~y ( ~y |y ~x |x ~y y )
else \ )
3 pick - >r \ n: ( c -= |y ( ~y |y ~x |x ) ( R: 1 'k 'c )
over x \ x += ~x ( ~y |y ~x |x ~x x )
then \ )
+! r> \ update x or y ( ~y |y ~x |x 'c ) ( R: 1 'k )
then \ )
loop \ ) ( ~y |y ~x |x 'c ) ( R: )
drop 2drop 2drop \ delete temps ( )
; \ ( )
Mike B.
[Edit: Ah, I think I found something ... I should be initializing my slope accumulator to |dx|-|dy| ... not sure if that's all, but I will have to test it later.]
I replied to Michael on comp.lang.forth:
https://groups.google.com/forum/#!topic ... 101-125%5DThis is what I said:
------------------------------------------------------------------------------------------------------------------------------
I skimmed over that thread. I didn't examine your code carefully.
I will say that line drawing is an example I have used previously in regard to how to write Forth code.
You don't want a function with a lot of parameters such as these:
: draw-line ( ax ay bx by -- ) ... ;
: intersect-lines ( ax ay bx by dx dy ex ey -- x y intersected? )
Having a lot of parameters quickly becomes a nightmare of stack-juggling. Using locals helps, but it is still horrible design.
You are better off to use structs:
0
w field .x
w field .y
constant point
0
point field .a
point field .b
constant line
point
w field .r
constant circle
: draw-line ( line -- ) ... ;
: intersect-lines ( lineA lineB -- point intersected? ) ... ;
In the above functions, line and lineA and lineB are all pointers to structs of type LINE. Structs are kept on the heap. The function INTERSECT-LINES returns a pointer to a struct of type POINT and a flag indicating if the lines intersected.
I have a lot of examples of structs in the novice-package. See LIST.4TH for some simple examples.
Here are some constructors (I haven't tested this, but it is pretty obvious and should work). Note that ALLOC is like ALLOCATE but does the error-checking rather than give you a flag.
: init-point ( x y node -- node )
>r
r@ .y !
r@ .x !
r> ;
: new-point ( x y -- node )
point alloc
init-point ;
: init-circle ( r x y node -- node )
init-point >r
r@ .r !
r> ;
: new-circle ( r x y -- node )
circle alloc
init-circle ;
Notice that the constructor is factored into INIT-xxx and NEW-xxx words. The purpose of this is so the INIT-xxx word can be used inside of the INIT-yyy word of a child class. In the above example, the CIRCLE class is a child of the POINT class. This is a crude form of OOP --- this is all I do in regard to OOP in the novice-package --- I have found that this works quite well for simplifying code.