6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon May 13, 2024 5:28 am

All times are UTC




Post new topic Reply to topic  [ 17 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Wed Feb 01, 2017 9:46 pm 
Offline

Joined: Sat Aug 21, 2010 7:52 am
Posts: 231
Location: Arlington VA
I wanted to do something in PETTIL, I can't remember precisely what it was. It might have been putting LEAVE inside of an IF/THEN block? The compiler, which I plagiarized from Blazin' Forth wouldn't let me, it would only complain of unbalanced control word pairs. So I rewrote all of the control word logic from the ground up, using Garth's excellent approach of DO pushing the exit address to the return stack before putting the loop limit and loop index there. I trimmed about 50 bytes from the compiler in doing so, mostly by factoring the heck out of it. In my actual code, only +/-PAIRS word is implemented as a primitive, rendered here in untested Forth. The rest are actual working code.

The word PAGEMARGIN encloses a call to PAGE in the dictionary when only a few bytes are left at the end of a page. C->S sign-extends a char to a 16-bit single. ?COMP aborts unless STATE is compiling.

Instead of putting the "word family" on the stack I used three counters that are initialized to 0 by ':' and tested for zero-ness by ';' The opener (e.g. IF) adds one to the counter. Middle words like ELSE or WHILE first decrement and then increment the counter, leaving it in the same state it started in but aborting if the counter goes negative (meaning there was no open conditional phrase in that family). The closers (e.g. THEN) decrement the counter.

METHOD is sort of a lite-objects thing I came up with. Put fields at the beginning of a child word and code immediately following, and the defining word retrives the fields to the stack, does what a Constructor would do in Java, then uses METHOD to transfer control to the child. I suppose it would even be possible to use a field to select from a few different code paths, so a child definition could have different behaviors (like QUAN words in MMSforth). For now it's just enough code to get the job done without working out yet another objects-in-Forth scheme.

?>MARK ?>RESOLVE are used to create and later resolve a forward branch.
?<RESOLVE creates and simultaneously resolves a backward branch.
Code:
create pairs 3 allot

: 0pairs   ( -- )   pairs 3erase ;
: ?nopairs   ( -- )   pairs 3c@ or [ 6 ] ?error ;
: +/-pairs   ( family -- |family| )   dup 0<
    if    negate pairs + dup -2 +!  then
    pairs + dup 1 +! 
    c@ c->s 0< [ 6 ] ?error ;

plusminuspairs
    stx z            ; stash the data stack pointer
    ldx tos
    bpl pluspairs
    dex
    txa
    eor #$ff         ; negate
    sta tos          ; return the absolute value
    tax
    dec pairs-1,x
    dec pairs-1,x
pluspairs
    inc pairs-1,x
    bmi pairs03
    ldx z
    jmp next
pairs03
    ldy #6              ; UNPAIRED CONDITIONALS
comperror
    jmp error


: method   ( addr -- )   rdrop 2- >r ;
: control   ( cfa family == ) ( -- cfa |family| )
   <builds
      , c,
   does>
      ?comp @+ swap c@+ +/-pairs swap method ; immediate
: control,   ( cfa -- )   [ $fc ] pagemargin , ;
: ?>mark   ( cfa family -- addr )   drop control, here 0 c, ;
: ?>resolve   ( addr -- )   here 1+ over - swap c! ;
: ?<resolve   ( addr cfa family -- ) drop control, dup>r here 2- c, r> ;
' (do) 3 control do    ] ?>mark ; immediate

which makes this:
(C:$008a) m ._do
>C:7428  20 d6 73 56  09 03 f1 73  ec 08
    jsr docontrol       ; the "does>" part of CONTROL
    .word pdo           ; (do)
    .byt 3              ; 3 = do-loop family
    .word _qtomark      ; ?>mark
    .word exit          ; semicolon compiles this

' (?do) 3 control ?do    ] ?>mark ; immediate
' (loop) -3 control loop    ] ?<resolve ?>resolve ; immediate
' (+loop) -3 control +loop    ] ?<resolve ?>resolve ; immediate
' unloop -3 control leave    ] +/-pairs drop xt, ; immediate
' (?leave) -3 control ?leave    ] +/-pairs drop xt, ; immediate
' ?branch 2 control if    ] ?>mark ; immediate
' branch -2 control else    ] +/-pairs ?>mark swap ?>resolve ; immediate
0 -2 control then    ] 2drop ?>resolve ; immediate
0 1 control begin    ] 2drop here 1- ; immediate
' ?branch -1 control while    ] +/-pairs ?>mark ; immediate
' ?branch -1 control until    ] q<resolve drop ; immediate
' branch -1 control again    ] ?<resolve drop ; immediate
' ?branch 2 control repeat    ] 2>r swap 2r> ?<resolve drop ?>resolve ; immediate


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 08, 2018 7:10 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
chitselb wrote:
I wanted to do something in PETTIL, I can't remember precisely what it was. It might have been putting LEAVE inside of an IF/THEN block? The compiler, which I plagiarized from Blazin' Forth wouldn't let me, it would only complain of unbalanced control word pairs.

Blazin' Forth does allow a LEAVE or ?LEAVE inside an IF/THEN block, even nested IF/THEN blocks. To the best of my knowledge, this is how Blazin' Forth does it. Blazin' Forth's DO and ?DO start a chain. The first LEAVE or ?LEAVE ( or ?DO ) Compiles a zero as a placeholder for the branch address. Each LEAVE or ?LEAVE after that compiles the previous place holder address as its place holder. LOOP and +LOOP resolve the forward references by following the chain back as each place holder is replaced with the correct address. So far so good. If LEAVE or ?LEAVE are in another control structure, even if they're not, LEAVE and ?LEAVE compile their corresponding primitive and work around other control structures by pushing a TRUE ( -1 ) on the return stack. each data stack item is then duplicated and moved to the return stack while the other copy is checked against the compiler security number for DO LOOPS. The current address on the data stack is compiled as the latest place holder and this place holder adress placed on the data stack. Items are moved from the return stack to the data stack until a TRUE ( -1 ) is removed.
If this sounds like it would use a lot of code in the compiler words, it does. I made a copy of the source for the Forth system for the Commodore 64 I'm working on and implemented Garth Wilson's idea for DO LOOPs. The new kernel was over a hundred bytes smaller. Thanks, Garth!

Cheers,
Jim


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 17 posts ]  Go to page Previous  1, 2

All times are UTC


Who is online

Users browsing this forum: No registered users and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: