Jump to content

Photo

Camel99 Forth Information goes here

Camel99 Forth Concatentive Programming ANS Forth

343 replies to this topic

#326 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Wed May 15, 2019 4:39 PM

For a little more clarity here is the actual code I used as my example.

EVENT1 CLR  R12         CRU base of the TMS9901 
       SBO  0           Enter timer mode 
       LI   R1,>3FFF    Maximum value
       INCT R12         Address of bit 1 
       LDCR R1,14       Load value 
       DECT R12         There is a faster way (see below) 
       SBZ  0           Exit clock mode, start decrementer 
      
 
EVENT2 CLR  R12 
       SBO  0           Enter timer mode 
       STCR R2,15       Read current value (plus mode bit)
       SRL  R2,1        Get rid of mode bit
       LDCR R12,15      Clear Clock register, and exit timer mode 
       S    R2,R1       How many cycles were done? 

My apparently unique use is to let the timer run continuously.

When I want to time something, I read the timer with code like EVENT2 , but I don't subtract anything, I simply store the timer value.

Then some time later (less than 349 mS) I just read the timer again, subtract both readings and get the ABS value.

 

Simple and faster than reloading each time.

 

Here is the actual code in RPN assembler. Notice I had to disable interrupts.

CODE: TMR!   (  -- )         \ load TMS9901 timer to max value 3FFF
             W 3FFF LI,      \ load scratch register W with MAXIMUM timer value
             R12 CLR,        \ CRU addr of TMS9901 = 0
             0 LIMI,
             0   SBO,        \ SET bit 0 to 1, Enter timer mode
             R12 INCT,       \ CRU Address of bit 1 = 2 , I'm not kidding
             W 0E LDCR,      \ Load 14 BITs from R1 into timer
             R12  DECT,      \ go back to address 0
             0    SBZ,       \ reset bit 0, Exits clock mode, starts decrementer
             2 LIMI,
             NEXT,           \ 16 bytes
             END-CODE

CODE: TMR@   ( -- n)         \ read the TMS9901 timer
             TOS PUSH,
             R12 CLR,
             0 LIMI,
             0 SBO,          \ SET bit 0 TO 1, ie: Enter timer mode
             TOS 0F STCR,    \ READ TIMER (14 bits plus mode bit) into W
             TOS  1 SRL,     \ Get rid of mode bit
             0 SBZ,          \ SET bit 1 to zero
             2 LIMI,
             NEXT,
             END-CODE


#327 mizapf OFFLINE  

mizapf

    River Patroller

  • 3,666 posts
  • Location:Germany

Posted Wed May 15, 2019 4:57 PM

BTW my source for the code was here:
 
http://www.unige.ch/...s9901.htm#Timer
 
The decrementer can be stopped by simply writing a zero to the leaving register, and leaving timer mode."

 
I'm afraid Thierry is wrong here. I used Tursi's program (slightly edited):
 

    DEF START

START LIMI 0
    CLR  R6
    LI   R7,>2000
    LI   R0,>3000   * Start value
    LI   R12,2
    SBO  -1         * Enter clock mode
    LDCR R0,14      * Load clock register

LP  SBO -1          * Enter clock mode
    STCR R5,14      * Read register
    SBZ -1          * Leave clock mode

    C R5,R6         * Keep highest value
    JL J1
    MOV R5,R6
J1  CLR R0
    MOVB R0,@>8C02
    MOVB R0,@>8C02

    MOV R5,R0
    BL @DIG

    MOVB R7,@>8C00

    MOV R6,R0
    BL @DIG

    TB  27          * Mouse button (R12=2) on the Geneve

    JEQ LP
    BLWP @0

* PRINT A HEX VALUE FROM R0
DIG   MOV  R0,R1
      LI   R3,4
DIGL  SRC  R1,12
      MOV  R1,R4
      ANDI R4,>000F
      MOVB @HEX(R4),@>8C00
      DEC  R3
      JNE  DIGL
      RT

HEX TEXT '0123456789ABCDEF'

    END

When you set the start value to 0000, the clock is still counting down. You can try this program for yourself, just leave away the TB 27 check when you run it on a TI-99/4A (it is the left mouse button on the Geneve; when I press it, the program exits).

 

So I turn on the clock mode, load the register with 0, and leave clock mode, and it still counts.



#328 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Wed May 15, 2019 6:17 PM

 
I'm afraid Thierry is wrong here. I used Tursi's program (slightly edited):

 

When you set the start value to 0000, the clock is still counting down. You can try this program for yourself, just leave away the TB 27 check when you run it on a TI-99/4A (it is the left mouse button on the Geneve; when I press it, the program exits).

 

So I turn on the clock mode, load the register with 0, and leave clock mode, and it still counts.

 

Ok thanks.  I have not actually tried loading the timer with 0 using my code.  All I knew what that it worked as expected on real iron.

 

I will write a version that lets me load the initial value interactively in Forth so I can play with it.



#329 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Thu May 16, 2019 1:24 PM

So I re-wrote my code and made it more like Tursi's in terms of setting up the CRU address. (it took 1 less instruction :))

 

I re-built Forth and put it on the old machine and sure enough the timer keeps running even when I load it with 0 as you can see in the screen shot.

CODE: TMR!   ( n -- )         \ load TMS9901 timer from stack
             0 LIMI,
             R12 CLR,        \ CRU addr of TMS9901 = 0
             0   SBO,        \ SET bit 0 to 1, Enter timer mode
             R12 INCT,       \ CRU Address of bit 1 = 2 , I'm not kidding
             TOS 0E LDCR,    \ Load 14 BITs from TOS into timer
            -1  SBZ,         \ reset bit 0, Exits clock mode, starts decrementer
             2 LIMI,
             TOS POP,
             NEXT,          
             END-CODE

CODE: TMR@   ( -- n)         \ read the TMS9901 timer
             TOS PUSH,
             0 LIMI,
             R12 2 LI,      \ cru = 1 (honest, 2=1)
            -1 SBO,         \ SET bit 0 TO 1, Enter timer mode
             TOS 0E STCR,   \ READ TIMER (14 bits)
            -1 SBZ,         \ RESET bit 1, exit timer mode
             2 LIMI,
             NEXT,
             END-CODE

Attached Files



#330 mizapf OFFLINE  

mizapf

    River Patroller

  • 3,666 posts
  • Location:Germany

Posted Thu May 16, 2019 1:41 PM

Usually, TI's specification documents are very precise, but this here is at least ambiguous. It led to several misunderstandings in the same way. Saying that a clock is "enabled" or "disabled" is normally understood as running or stopped. In fact, the formulation glitches already start with the name "clock mode" which could make you think you have to turn on this mode to run the clock, but this mode is rather used to read or write the clock register, while the clock is running in interrupt mode.

 

And there are some more open questions that I will need to check to make sure:

 

- A soft reset (SBZ 15 in clock mode) resets all I/O ports to input. Does it also reset the interrupt mask? (not explicitly stated)

- If you set a port to output mode (e.g. P15), can it trigger the interrupt line with which it shares the pin (/INT7)? (not explicitly stated)

 

Raphael Nabet (the original author of the TI emulation in MESS) assumed that the latter is not possible; but I'll try to test it on my Geneve. The point is that I can rewrite the interrupt handler on the Geneve, as it resides in RAM. On the TI, if the interrupt source is not the VDP, the handler searches the DSRs.



#331 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Thu May 16, 2019 8:01 PM

Step 1 to generating native 9900 code from Forth.

 

I have ripped up parts of the XFCC99  Forth cross-compiler and kept other parts and created NATIVE99 , the beginning a Forth cross-compiler that generates native code.

It's still very manual.  I can compile colon definitions but they don't know how to call themselves yet.

There is no Forth dictionary, that is all kept in the PC more like a C compiler would do. Most of the Forth primitives compile inline at the moment but nothing is compiled into the binary unless you use it in a program, so the programs can be very small. 

 

The spoiler has the first program which is displaying all the dirty details.

(but it works as expected) ;)

 

Here is the compiler summary

Program Summary:
  A000  40960 Load address
    90    144 Code size
  A074  41076 boot address
   116    278 Image size
     4      4 Code words
     0      0 Forth words
Spoiler


#332 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Fri May 17, 2019 6:39 AM

Step 2:  Colon definitions call themselves

 

In Step 1 we used a sub-routine definer aliased as colon to create Forth words that had to be explicited called.

 

In step 2 we defined a proper <DOCOL> routine with CREATE DOES>. 

CREATE lets us define the compile-time activity.

DOES> lets us define what happens when we run the WORD that CREATE created, if that makes sense.  It's like a simple object constructor.

\ Native code COLON COMPILER

: <DOCOL> ( n -- )
            CROSS-ASSEMBLING
            CREATE           \ create the word in compiler dictionary
                  THERE ,    \ remember my address in compiler Forth
                  R11 RPUSH, \ compile TI-99 entry code into target program

           \ runtime:
            DOES>  @  @@ BL, \ fetch my address, branch&link indirect
;

\ define the cross-compiler's TI-99 colon and semi-colon
CROSS-ASSEMBLING
HOST: :       TFORTHWORDS [ FORTH ] 1+! \ count the word for reporting
              <DOCOL>  ;HOST

HOST: ;        RET,  ENDSUB  ;HOST

The spoiler has the new sub-program that no longer needs CALL.

 

Spoiler

 



#333 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Sat May 18, 2019 8:53 AM

Native Code Compiler (NCC) Preliminary Performance Comparison

 

It is clear to me now that I will need to make a much more sophisticated compiler to come close to Assembler performance.

Here is a comparison of three Forth compilers and the equivalent written in Forth assembler.

: TEST  FFFF
        BEGIN
           1- DUP
        0= UNTIL
        BYE ;

Forth Assembler that does the same thing.

         TOS PUSH,
         TOS 0FFFF LI,
         BEGIN,
            TOS DEC,
         EQ UNTIL,
         BYE

ITC  10.9 sec
DTC  8.6 sec
NCC  4.4 sec   ( all inline primitives)
ASM  < 1 sec ;-)

 

Some obvious points to optimize...

  • 1- DUP  should be smarter to remove the TOS POP from 1- so the DUP is not needed. This would become TOS DEC,
  • 0= UNTIL  should be optimized to a JNE @BEGIN  

This is called "peephole" optimization in Forth compilers so that is where the focus has to be to make this closer to ASM code.

 

Edit:  I should add that this is the worst case example. 

         In real programs ITC Forth tends to be 3 to 4 times slower than optimized compilers as seen in the Benchmarking languages thread.


Edited by TheBF, Sat May 18, 2019 9:40 AM.


#334 Lee Stewart ONLINE  

Lee Stewart

    River Patroller

  • 3,990 posts
  • Location:Silver Run, Maryland

Posted Sat May 18, 2019 11:30 AM

I cannot speak to the NCC for fbForth, but, substituting DROP for BYE so I do not need to reload everything while testing, TEST becomes in fbForth (12.7 seconds)

HEX
: TEST  FFFF
   BEGIN
      1- DUP
   0= UNTIL
   DROP  ;

which is ~1.5 seconds slower than (11.2 seconds)

HEX
: TEST2  FFFF DUP
   BEGIN
   WHILE
      1- DUP
   REPEAT
   DROP  ;

which, not needing the stack in fbForth ALC, becomes (~1 second)

HEX
ASM: TEST3
   R0 FFFF LI,
   BEGIN,
      R0 DEC,
   NE WHILE,
   REPEAT,
;ASM

Using BEGIN, ... UNTIL, (as in your code) saves one ALC instruction and is marginally faster (10%?—stopwatch timing):

HEX
ASM: TEST4
   R0 FFFF LI,
   BEGIN,
      R0 DEC,
   EQ UNTIL,
;ASM

...lee



#335 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Sat May 18, 2019 1:41 PM

Thanks Lee,

 

That confirms my results.  Yes now I remember, you have the BYE that writes to low RAM.  Mine is just 2 instructions.

 

The problem with the NCC concept at the moment is that to compute 0= as a primitive it is  a TOS TOS MOV,  and then set/reset the TOS register appropriately,

followed by UNTIL which is another TOS TOS MOV, and then drop the TOS and jump back or jump forward, so it is a pile of instructions on the Forth VM.

I am going to see if I can make something smarter for 0=  like is done in the Forth Assembler and potentially use that idea for branching as well.

 

It's a challenge to do it really efficiently without leveraging the machines native branching but Forth uses the TOS as the status flag...

 

It might be better to just create a bunch of machine macros like @, !, etc and use ALC. :-)

 

Without the Forth headers in the code  however you save about 25% of the space so that leaves room for code bloat due to the VM concept.

 

I am going to take a page from TI-Forth and put the return macro in a register so  RET, will be as below.  This will save me 2 bytes per colon definition.

Sub-routines (colon defs.) are just called with BL because each colon definition begins by pushing R11 onto the rstack.

CODE RET,   *R10 B ,

\ R10 will contain this code located in scratchpad RAM
     *RP+ R11 MOV,
         *R11 B,

Ultimately if I can get it in reasonable shape it would be fun to generate a working Forth kernel that is all native code. 

It will be bigger than 8K I am sure, but maybe only 50% if make it smart enough. 

 

In the shorter term I want to make it complete enough to compile some real program and see how it all works and how big it is.

I should be able to do the Sieve benchmark in a little while.

(He said over-optimistically)

 

 

Anyway its a good education.  Thanks for following the progress.

 

How's that knee?

 

 

 

 

 

 

 



#336 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Sat May 18, 2019 1:48 PM

Native Code Compiler (NCC) Preliminary Performance Comparison

         SP DECT,
         R4 SP+ MOV,   ( PUSH, macro)
         R4 0FFFF LI,
         BEGIN,
            R4 DEC,
         EQ UNTIL,
         BYE

 

BTW for clarity my assembler code is actually this, because TOS is just an alias for R4 in my Forth assembler and PUSH, is a 2 instruction macro.

So our ALC tests are functionally the same.

 

When I first started this project I was so confused I used every trick I could to simplify the code.  ;-)



#337 Lee Stewart ONLINE  

Lee Stewart

    River Patroller

  • 3,990 posts
  • Location:Silver Run, Maryland

Posted Sat May 18, 2019 5:25 PM

. . .

Anyway its a good education.  Thanks for following the progress.

 

How's that knee?

 

The knee (1 year post-surgery) is doing well, as is the other one (2 years post-surgery)!  :)   Thanks for asking.

 

...lee



#338 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Sun May 19, 2019 9:45 AM

NCC Program 3

 

Here is something that I didn't expect.  In native code the Forth colon is just is an Assembly language sub-routine.

I was looking at the docs for MeCrisp Forth which is a native code Forth for a number of processors. I was shocked to see the colon used with Assembly language inside.

Guess what?  It works in my crude compiler as well.

 

This program compiles to 112 bytes. :-D

\ Native99 test program 3

CROSS-COMPILING
\ Compiler pre-amble
         START.                \ sets a timer
         NEW.                  \ init target memory to FFFF
         A000 ORIGIN.
         TI-99.EA5

[CC] INCLUDE CC9900\NATIVE\NCFORTH.FTH
[CC] INCLUDE CC9900\NATIVE\NCPRIMS.FTH
[CC] INCLUDE CC9900\NATIVE\NCCOLON.FTH

[CC] HEX

CROSS-ASSEMBLING
FFFF CONSTANT >FFFF

: FTEST
       >FFFF
       BEGIN
         1- DUP
      0= UNTIL
;

: ATEST
        TOS PUSH,
        TOS FFFF LI,
        BEGIN,
          TOS DEC,
        EQ UNTIL,
;

PROGRAM: RUN               \ sets the entry address
         8300 WORKSPACE
         FF00 RSTACK
         FEA0 DSTACK
         FTEST
         ATEST
         BYE
END.

Edited by TheBF, Sun May 19, 2019 9:47 AM.


#339 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Sun May 19, 2019 10:36 AM

NCC Program 4  Keyboard

 

This is become very interesting. :)

 

I stole code from CAMEL99 Forth and created Forth KEY very quickly.

\ Native99 test program 4  Keyboard interface

CROSS-COMPILING
\ Compiler pre-amble
         START.                \ sets a timer
         NEW.                  \ init target memory to FFFF
         A000 ORIGIN.
         TI-99.EA5

\ first build the compiler
[CC] INCLUDE CC9900\NATIVE\NCFORTH.FTH
[CC] INCLUDE CC9900\NATIVE\NCPRIMS.FTH
[cc] INCLUDE CC9900\NATIVE\NCCOLON.FTH

CROSS-ASSEMBLING

: KEY?  ( -- char | 0)
        TOS PUSH,
        TOS CLR,            \ TOS is output
        0 LIMI,
        83E0 LWPI,          \ switch to GPL workspace
        000E @@ BL,         \ call ROM keyboard scanning routine
        8300 LWPI,          \ return to Forth's workspace , interrupts are restored
        2 LIMI,
        837C @@ R0 MOVB,    \ read GPL status byte (=2000 if key pressed)
        NE IF,
            8374 @@ TOS MOV, \ read the key into TOS (R4)
        ENDIF,
;

: KEY   ( -- char)
        BEGIN
          KEY?
          ?DUP
        UNTIL
;

PROGRAM: RUN               \ set the entry address
         8300 WORKSPACE
         FF00 RSTACK
         FEA0 DSTACK

         KEY DROP          \ wait for key & drop it
         BYE               \ return to boot screen
END.

And here is the compiled code from CLASSIC99 with my comments

EDIT: Found a bug with ?DUP

A05C  02E0  lwpi >8300         * workspace
A060  0207  li   R7,>ff00      * return stack pointer
A064  0206  li   R6,>fea0      * data stack pointer
A068  06A0  bl   @>a040        * call RUN
A040  0647  dect R7            * enter sub-routine
A042  C5CB  mov  R11,*R7
A044  06A0  bl   @>a014        * call "key?"          
A048  C104  mov  R4,R4         * ?DUP (inline)        
A04A  1601  jeq  >a050       *** FIXED THIS JUMP         
A04C  0646  dect R6            * DUP (inline)        
A04E  C584  mov  R4,*R6        *           
A050  C104  mov  R4,R4         * UNTIL (inline)        
A052  1602  jne  >a058                 
A054  C136  mov  *R6+,R4               
A056  10F6  jmp  >a044                 
A058  C2F7  mov  *R7+,R11      * ';' (inline)         
A05A  045B  b    *R11                 

Edited by TheBF, Sun May 19, 2019 11:08 AM.


#340 Lee Stewart ONLINE  

Lee Stewart

    River Patroller

  • 3,990 posts
  • Location:Silver Run, Maryland

Posted Sun May 19, 2019 11:06 AM

I do not suppose there is any easy way to remove superfluous line A050—perhaps through “peephole” optimizing?

 

...lee



#341 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Sun May 19, 2019 11:52 AM

I do not suppose there is any easy way to remove superfluous line A050—perhaps through “peephole” optimizing?

 

...lee

That's the challenge for sure.  At the moment this is a very naive compiler.

 

My strategy is to get a lot of working before attempting to get clever.  I am working at my personal "bleeding edge" as it is.  :-)



#342 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Sun May 19, 2019 8:43 PM

I do not suppose there is any easy way to remove superfluous line A050—perhaps through “peephole” optimizing?

 

...lee

 

I just tried this and it failed but... I removed  MOV R4,R4 from the 0= code and it works. This is because the DUP is operating on the EQ flag.

It also goes faster.  3.99 seconds by the armstrong method.

So your idea was a good one.  Thanks!

HOST: 0=    ( n -- ? )   
\      TOS TOS MOV,
        3  JEQ,
        TOS CLR,
        2  JMP,
        TOS SETO, ;HOST IMMEDIATE
HOST: UNTIL    ( n --)
             TOS TOS MOV,  \ test tos=0
             3 JNE,        \ if tos=0
             TOS POP,      \ drop n
             BACK JMP,     \ loop
             TOS POP,      \ elseif tos<>0, drop
             ;HOST IMMEDIATE

I just stole some stuff from the TI-FORTH assembler that we all use to create BEGIN WHILE REPEAT and it seems to work.

Removed compile time error detection for now to keep the code easier to understand.

i might have to add a DROP in here some where.

EDIT: Yes IF needed a DROP (TOS POP,)   and so did THEN, because you need to drop the TOS if you take the jump AND if you don't take the jump just like UNTIL above.

HOWEVER,  If you make a word called DUPWHILE the decrementing WHILE loop executes in 1 second vs 4 seconds.   Worth adding that to the peephole optimizer.

\ Branch calculators taken from TI-FORTH Assembler
 HOST: AHEAD   ( -- addr)  THERE 2-      ;HOST
 HOST: RESOLVE ( addr -- ) THERE OVER - 2- 2/ SWAP 1+ TC!   ;HOST

\ here we use parts of the Assembler directly
 HOST: IF    ( n --)  NE CJMP AHEAD  TOS POP,  ;HOST IMMEDIATE
 HOST: ELSE  ( -- )   0 JMP,  RESOLVE          ;HOST IMMEDIATE
 HOST: THEN  ( addr -- )  RESOLVE   TOS POP,   ;HOST IMMEDIATE

HOST: WHILE  ( ) POSTPONE IF 2+  ;HOST IMMEDIATE
HOST: REPEAT
          >R     POSTPONE AGAIN
          R>  2- POSTPONE THEN
          ;HOST  IMMEDIATE


Edited by TheBF, Sun May 19, 2019 9:37 PM.


#343 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Yesterday, 5:46 AM

I am having so much fun with this that I woke up early.
 
So once I figured out how to do WHILE loops it was simple to make a Chuck Moore style FOR NEXT loop. 
 
When Chuck starting building Forth architecture CPUs he found that it was always faster to just run a down counter for a loop. He stopped using DO/LOOP.
He made FOR NEXT which in it's simplest form loads a number somewhere and NEXT decrements the number and loops WHILE the number<>0.
We do this all the time in Assembly Language.
This version uses the return stack to hold the number, it could make a little faster by using a spare register.
 
So with the WHILE structure as an example, here is all it took to add FOR/NEXT

 

EDIT: The empty FOR/NEXT loop with 64K iterations runs in 1 second.  Less if we put the loop counter in a register.

HOST: FOR   TOS RPUSH,   
            TOS POP,    \ refill data stack cache register
            THERE       \ leave compiler's current working address on PC Forth stack
;HOST  IMMEDIATE

HOST: NEXT  *RP DEC,
             NE CJMP AHEAD 2+  \ same as WHILE but no need to DROP data stack
            >R BACK JMP,       \ loop not finish, jump back to THERE
             R> 2- RESOLVE     \ compute the address need by AHEAD and put it in the code
             RP INCT,          \ drop the index from the return stack
;HOST IMMEDIATE

Here is how it is used (compiler pre-amble removed)

CROSS-ASSEMBLING
0 CONSTANT 0
1 CONSTANT 1
2 CONSTANT 2
FFFF CONSTANT >FFFF

VARIABLE X

PROGRAM: RUN               \ set the entry address
         8300 WORKSPACE
         FFFF RSTACK
         FF00 DSTACK
         
         FFFF FOR
            X 1+!
         NEXT
         BYE
END.

Edited by TheBF, Yesterday, 5:52 AM.


#344 TheBF OFFLINE  

TheBF

    Stargunner

  • Topic Starter
  • 1,044 posts
  • Location:The Great White North

Posted Yesterday, 10:01 AM

It was trivial to use R9 as the loop counter so I just did it.   R9  is the IP register in threaded Camel99 Forth but we don't need that anymore. :-)

 

By pushing R9 onto the return stack when we enter FOR and RPOPing it when we leave FOR/NEXT is nestable.

A 64K loop nested 10 times takes about 9 seconds so each 64K loop is  900mS. Not to shabby!

HOST: FOR   R9  RPUSH,    \ R0 will be the loop counter
            TOS R9 MOV,
            TOS POP,
            THERE
;HOST  IMMEDIATE

HOST: NEXT   R9 DEC,
             NE CJMP AHEAD 2+  \ while *RP<>0
             >R BACK JMP,
             R> 2- RESOLVE
             R9 RPOP,
;HOST IMMEDIATE








Also tagged with one or more of these keywords: Camel99, Forth, Concatentive Programming, ANS Forth

0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users