Jump to content
Airshack

Why twice interpreted TI BASIC?

Recommended Posts

19 minutes ago, InsaneMultitasker said:

I think we may need to bring in an interpreter. 

We might need two of them.

  • Thanks 1
  • Haha 4

Share this post


Link to post
Share on other sites
23 minutes ago, InsaneMultitasker said:

I think we may need to bring in an interpreter. 

 

tenor.gif

  • Like 1
  • Haha 1

Share this post


Link to post
Share on other sites
18 hours ago, Tursi said:

Wrt the Forth comments - it's true there are some similarities. Forth words are converted to values. It's often called compilation but it's actually the same mechanical steps as tokenizing. However, there are two differences to the TI BASIC debate.

 

First, there's only one layer. The Forth tokens are executed directly.

 

Secondly, it's not interpreted. As I understand it, the tokens are literally already the jump addresses of the machine instructions. It's not assembly and it's not interpreted byte code, it really is its own thing.

Hmm per TI Term and what I know about GPL all commands are jump addressed just like Forth?

This is just a random snippet of GPL in Assembly and sure looks like Jump codes to me?

GPL PUSH:
016E B80E AB 14,@>8372 Increase data stack pointer(Attention system flag)
0170 8372
0172 D1A0 MOVB @>8372,6 Fetch pointer
0174 8372
0176 0986 SRL 6,8
0178 D9A0 MOVB @>83E5,@>8300(6) Push byte on data stack
017A 83E5
017C 8300
017E 0460 B @>0070 and go on
0180 0070
GPL DECT:
0182 09E0 SRL 0,14
GPL INCT:
0184 0600 DEC 0 -1
GPL INC und SUB:
0186 0500 NEG 0 Negate
GPL DEC und ADD:
0188 D145 MOVB 5,5
018A 134B JEQ >0222 Byte?, then execute
018C A080 A 0,2 Add word
018E 104C JMP >0228 Execute
GPL AND:
0190 0540 INV 0 Invert
0192 4080 SZC 0,2 Execute AND
0194 1049 JMP >0228 and execute
GPL OR:
0196 E080 SOC 0,2 Or
0198 1047 JMP >0228 Execute
GPL XOR:
019A 2880 XOR 0,2 Exclusive OR
019C 1045 JMP >0228 Execute
GPL ST:
019E C080 MOV 0,2 R0 in R2
01A0 1046 JMP >022E Execute
GPL EX:
01A2 C242 MOV 2,9 Save data S
01A4 C080 MOV 0,2 Data D in Data S
01A6 06A0 BL @>0232 D write new data
01A8 0232
01AA 06C4 SWPB 4 Exchange VDP flag
01AC C0C1 MOV 1,3 Exchange address
01AE 101B JMP >01E6 Restore old data and execute
GPL SRA:
01B0 0802 SRA 2,0 Shift to R0
01B2 103D JMP >022E Execute
GPL SLL:
01B4 0A02 SLA 2,0
01B6 103B JMP >022E
GPL SRL:
01B8 D145 MOVB 5,5 No word?
01BA 1601 JNE >01BE
01BC 7082 SB 2,2 Hbyte 0
- 16

 

Share this post


Link to post
Share on other sites
On 6/23/2021 at 2:00 PM, RXB said:

Forth does exactly the same thing in interpreter mode, Forth is called that runs Assembly to function. GPL = Forth in how they work.

 

Well, the console GPL interpreter is more than 300 bytes of code that determines what type of instruction the retrieved byte is, determines how many arguments (0, 1 or 2) must be retrieved, retrieves those arguments, looks up the instruction’s execution address in a table, and, finally branches to that looked-up address.

 

The interpreter for Indirectly threaded fbForth does not do any preparation nor does it do table lookup to get an execution address. It is passed a list of one or more addresses that point to executable code (no lookup required—just branching to the code). As @TheBF mentioned, this Forth interpreter is only three instructions at 6 bytes (special registers are explained in the spoiler):

Spoiler
SP   =  R9   <--Parameter Stack Pointer
W    =  R10  <--Inner Interpreter current Word Pointer
LINK =  R11  <--LINKage for subroutines in CODE routines
IP   =  R13  <--Interpretive Pointer
R    =  R14  <--Return Stack Pointer

 

$NEXT  MOV  *IP+,W    \   Forth
DOEXEC MOV  *W+,R1     >    Address
       B    *R1       /       Interpreter

Because much of Forth is colon definitions, the code fields of : ( DOCOL ) and ; ( $SEMIS ) are included in highspeed, scratchpad RAM (16 bytes total):

DOCOL  DECT R
       MOV  IP,*R
       MOV  W,IP
$NEXT  MOV  *IP+,W    \   Forth
DOEXEC MOV  *W+,R1     >    Address
       B    *R1       /       Interpreter
$SEMIS MOV  *R+,IP
       JMP  $NEXT

Because an additional chunk of fbForth is defined with <BUILDS ... DOES> for variables, constants, etc., the code field of DOES> ( DODOES ) is also included (22 bytes total):

DODOES DECT SP
       MOV  W,*SP
       MOV  LINK,W
DOCOL  DECT R
       MOV  IP,*R
       MOV  W,IP
$NEXT  MOV  *IP+,W    \   Forth
DOEXEC MOV  *W+,R1     >    Address
       B    *R1       /       Interpreter
$SEMIS MOV  *R+,IP
       JMP  $NEXT

Finally, because it is executed so often, the address interpreter is duplicated after $SEMIS for speed (26 bytes total):

DODOES DECT SP
       MOV  W,*SP
       MOV  LINK,W
DOCOL  DECT R
       MOV  IP,*R
       MOV  W,IP
$NEXT  MOV  *IP+,W    \   Forth
DOEXEC MOV  *W+,R1     >    Address
       B    *R1       /       Interpreter
$SEMIS MOV  *R+,IP
       MOV  *IP+,W    \   Duplicated Forth
       MOV  *W+,R1     >    Address
       B    *R1       /       Interpreter

Each new word in Forth is an extension of the language, executed just like any other part of Forth. which is not so for GPL, TI Basic, XB, RXB, etc.

 

...lee

Share this post


Link to post
Share on other sites
1 hour ago, Lee Stewart said:

 

Well, the console GPL interpreter is more than 300 bytes of code that determines what type of instruction the retrieved byte is, determines how many arguments (0, 1 or 2) must be retrieved, retrieves those arguments, looks up the instruction’s execution address in a table, and, finally branches to that looked-up address.

 

The interpreter for Indirectly threaded fbForth does not do any preparation nor does it do table lookup to get an execution address. It is passed a list of one or more addresses that point to executable code (no lookup required—just branching to the code). As @TheBF mentioned, this Forth interpreter is only three instructions at 6 bytes (special registers are explained in the spoiler):

  Reveal hidden contents

SP   =  R9   <--Parameter Stack Pointer
W    =  R10  <--Inner Interpreter current Word Pointer
LINK =  R11  <--LINKage for subroutines in CODE routines
IP   =  R13  <--Interpretive Pointer
R    =  R14  <--Return Stack Pointer

 

$NEXT  MOV  *IP+,W    \   Forth
DOEXEC MOV  *W+,R1     >    Address
       B    *R1       /       Interpreter

Because much of Forth is colon definitions, the code fields of : ( DOCOL ) and ; ( $SEMIS ) are included in highspeed, scratchpad RAM (16 bytes total):

DOCOL  DECT R
       MOV  IP,*R
       MOV  W,IP
$NEXT  MOV  *IP+,W    \   Forth
DOEXEC MOV  *W+,R1     >    Address
       B    *R1       /       Interpreter
$SEMIS MOV  *R+,IP
       JMP  $NEXT

Because an additional chunk of fbForth is defined with <BUILDS ... DOES> for variables, constants, etc., the code field of DOES> ( DODOES ) is also included (22 bytes total):

DODOES DECT SP
       MOV  W,*SP
       MOV  LINK,W
DOCOL  DECT R
       MOV  IP,*R
       MOV  W,IP
$NEXT  MOV  *IP+,W    \   Forth
DOEXEC MOV  *W+,R1     >    Address
       B    *R1       /       Interpreter
$SEMIS MOV  *R+,IP
       JMP  $NEXT

Finally, because it is executed so often, the address interpreter is duplicated after $SEMIS for speed (26 bytes total):

DODOES DECT SP
       MOV  W,*SP
       MOV  LINK,W
DOCOL  DECT R
       MOV  IP,*R
       MOV  W,IP
$NEXT  MOV  *IP+,W    \   Forth
DOEXEC MOV  *W+,R1     >    Address
       B    *R1       /       Interpreter
$SEMIS MOV  *R+,IP
       MOV  *IP+,W    \   Duplicated Forth
       MOV  *W+,R1     >    Address
       B    *R1       /       Interpreter

Each new word in Forth is an extension of the language, executed just like any other part of Forth. which is not so for GPL, TI Basic, XB, RXB, etc.

 

...lee

Lee you taking Compiled or Interpreted here?

Looking at Forth Blocks files does not show address they look like text.

Hmm GPL and XB  has a definition table of tokens too.

It just unlike Forth does allow additions to that table.

Well Subprograms in XB kind of do that but that is not GPL is it.

Share this post


Link to post
Share on other sites

Forth is neither compiled nor interpreted.

 

Compiled means that the output code is directly executable assembly (usually).
Interpreted means that code examines the tokens and takes action based on the examination. That's what GPL does.

Forth reads the value and then jumps to it.

 

That's all /post/ source code, of course.

 

BASIC "tokenizes" its source code and generates a byte code which is later interpreted - this is mostly to save memory rather than for performance. BASIC is the classic example of an interpreter as most implementations have used this approach.


Assemblers "assemble" their source code into executable opcodes. This is different from compilation because it's a one-to-one mapping. (Usually). 

GCC "compiles" its source code, turning keywords and sequences of keywords into technically equivalent sequences of assembly language, but there is often no direct mapping between a source line (even something simple as "i++") and the output assembly (which can be optimized depending on circumstances to many different outputs, or even dropped altogether if the compiler decides it's not important.)

Forth seems to use the term "compile", but it does a one-to-one mapping from source code to function addresses. The resulting code is not itself executable, but it's literally as close as you can get, as Lee demonstrates with the "interpreter" needing only three instructions. They call this "threaded execution" and I'm willing to agree it deserves its own term, since it's not interpreted (ie: no decisions are made based on the word read) and it's not compiled (the CPU can't execute it as native opcodes).

Of course, if it was stored in memory as BL @>1234, BL @>3456, etc... that would have the same effect but then it WOULD be compiled, since the result is executable. But it would also take twice as much memory to store. And like everything else in computing, you can easily cloud the issue by citing any of hundreds of examples of exceptions. ;)

 

  • Like 4

Share this post


Link to post
Share on other sites

 

QUOTE:

Of course, if it was stored in memory as BL @>1234, BL @>3456, etc... that would have the same effect but then it WOULD be compiled, since the result is executable. But it would also take twice as much memory to store. And like everything else in computing, you can easily cloud the issue by citing any of hundreds of examples of exceptions. ;)

 

So this is similar to XML commands as single byte commands in GPL right?

For that matter Assembly can also call these XML commands that are built in.

 

6010 or 6030 has single byte XML routines that work like BL @>6088 or BL @>00F8

Share this post


Link to post
Share on other sites
1 hour ago, Tursi said:

Forth is neither compiled nor interpreted.

 

Compiled means that the output code is directly executable assembly (usually).
Interpreted means that code examines the tokens and takes action based on the examination. That's what GPL does.

Forth reads the value and then jumps to it.

 

That's all /post/ source code, of course.

 

BASIC "tokenizes" its source code and generates a byte code which is later interpreted - this is mostly to save memory rather than for performance. BASIC is the classic example of an interpreter as most implementations have used this approach.


Assemblers "assemble" their source code into executable opcodes. This is different from compilation because it's a one-to-one mapping. (Usually). 

GCC "compiles" its source code, turning keywords and sequences of keywords into technically equivalent sequences of assembly language, but there is often no direct mapping between a source line (even something simple as "i++") and the output assembly (which can be optimized depending on circumstances to many different outputs, or even dropped altogether if the compiler decides it's not important.)

Forth seems to use the term "compile", but it does a one-to-one mapping from source code to function addresses. The resulting code is not itself executable, but it's literally as close as you can get, as Lee demonstrates with the "interpreter" needing only three instructions. They call this "threaded execution" and I'm willing to agree it deserves its own term, since it's not interpreted (ie: no decisions are made based on the word read) and it's not compiled (the CPU can't execute it as native opcodes).

Of course, if it was stored in memory as BL @>1234, BL @>3456, etc... that would have the same effect but then it WOULD be compiled, since the result is executable. But it would also take twice as much memory to store. And like everything else in computing, you can easily cloud the issue by citing any of hundreds of examples of exceptions. ;)

 

 

All good with an update to bring everyone up to the last 30 years of work in Forth.

 

Old fashioned Forth is not "compiled" as you say. (It is a programmable compiler so if you want to compile native code you can and we do) :) 

(My MForth99 does that but it's not ready for prime time yet) 

 

In the early 1980s many people were building sub-routine based Forths and inlining intrinsic instructions  and doing peephole optimizatin. This is compiled code and it is still interactive.

 

In the 1980s Chuck Moore built his own CPUs and CMForth for those machines compiled native code. It was easy since the instruction set was 31 Forth instructions. 

He then created machine Forth for intel that he runs as the O/S on his laptop. :) 

 

Since then there have been numerous native code Forth compilers. They are just a bit hard for hobbyists to create so everybody copies the work from the 1970s.

With one exception that I know of: The iForth FAQ (iae.nl) 

Marcel is a P Eng and uses his system for his own work.

 

Mops Forth was an OOP native Forth for MC68000  circa 1989

 

SwiftForth  USA, multiple cross-compilers, Windows, OS/X and Linux Native code compilers   SwiftForth IDE for Windows, Linux, macOS

 

MPE VFX Forth U.K.  Fastest generated code of any Forth compiler. Compares to C but without the "nasal demons".

: VFX forth ;

 

Bottom line: all commercial Forth systems are native code compilers since ~year 2000.

 

I am working to get TI-99 into the present :) 

 

 

 

  • Like 3

Share this post


Link to post
Share on other sites
19 hours ago, Lee Stewart said:

The interpreter for Indirectly threaded fbForth does not do any preparation nor does it do table lookup to get an execution address. It is passed a list of one or more addresses that point to executable code (no lookup required—just branching to the code). As @TheBF mentioned, this Forth interpreter is only three instructions at 6 bytes (special registers are explained in the spoiler)

Sorry I don't understand Forth. Where is the list passed from? Are there no parmeters or arguments for any of the tokens? How does the program loop?

  • Like 2

Share this post


Link to post
Share on other sites
3 hours ago, Asmusr said:

Sorry I don't understand Forth. Where is the list passed from? Are there no parameters or arguments for any of the tokens? How does the program loop?

 

Words in indirect threaded Forth are defined in terms of other words (high-level definitions) or as small (low-level) ALC routines.

 

The high-level words are merely lists of execution addresses that the inner, address interpreter walks (pops). High-level words in that list push the next execution address pointer onto the return stack before walking the list in the new, current word. The return stack is popped to get the next execution address, and on it goes to the end of the original, executing word. When the execution address list(s) is(are) exhausted, the outer, text interpreter, which looks for new words or numbers from the console or currently loading blocks, is restarted.

 

The low-level words are executed, returning immediately to the address interpreter, which grabs the next, waiting execution address in the list.

 

If a word requires parameters/arguments, that word handles everything. The interpreter knows nothing of parameters/arguments.

 

...lee

  • Like 2

Share this post


Link to post
Share on other sites
2 hours ago, Asmusr said:

Sorry I don't understand Forth. Where is the list passed from? Are there no parmeters or arguments for any of the tokens? How does the program loop?

Lee beat me to the punch but here is a bit more info.

 

(If you really want to know the dirty details, JonesForth is a source code for a Forth system on Intel that has a running commentary on the source code. A very good primer)

 

30,000 foot view:

 

In the traditional Forth that we run on TI-99 every hi-level routine in the language is made of these lists of addresses. 

 

Underpinning everything there are some assembly language routines that can be called "primary" routines, also called "CODE words" by Forthers.

( minimum number I have seen is 31, Camel99 has 138 to get extra speed)

 

There are no registers per-se.  The data stack does the role of registers  and there is a separate return stack for sub-routine returning. (sounds like GPL) 

 

So for example here are two primary routines.

CODE  DUP   (blah blah blah )   * duplicates the top item on the data stack. Written in Assembler

CODE +      ( blah blah lah  )     * add the 2nd item on the stack to the top of the stack, leave result on top of stack 

 

If we wanted to make a routine to compute X * 2   we would make this in Forth source code:

 

:  2TIMES      DUP  +  ; 

 

With Lees code in mind something like this gets "compiled"  into memory. (different on various systems)  

 

<link-to-previous-word> <6> <2TIMES> <address of DOCOL>  <address of DUP> , <address of +>  <address of semi-colon>

 

(  <6> is the length of the string )

 

Lees DOCOL code begins processing the list that follows it in the Forth word.

It gets DUP and runs the ASM code there and returns to $NEXT

It gets +  and runs the ASM code there and returns to $NEXT

It gets SEMIS and runs the ASM code there which ends the list.

 

There are a lot details omitted but this gives you some context. 

 

Are there no parameters or arguments for any of the tokens?

Arguments come from the data stack and are typically returned to the data stack.

You are also free to create variables which are just  return a memory address like an ASM label.

There are also CONSTANTS in the language but there are no other data structures in the core language.

Memory outside of the Forth code is as free to use as assembler so make what you want was the philosophy.

Now days there libraries for structs and queues and linked-lists  and dynamic allocation.

 

"How does the program loop?" 

 

It is a virtual machine (VM) and one of the CPU registers is used as the Interpreter pointer. (IP)

To loop the VM you just have to change the address in the IP register.

 

Typically there two CODE words for branching:

 

BRANCH   branch unconditionally

?BRANCH branch if the top of stack is zero.  (sometimes called 0BRANCH) 

 

These are used to create all the branching and looping words in the language with some clever "compiling" :) 

 

 

 

 

 

 

 

 

  • Like 3

Share this post


Link to post
Share on other sites
Posted (edited)

Understood, but how do you get a value pushed onto the data stack that wasn't there before, like the number 65? Edit: I assume BRANCH sets the IP to a value it pops from the data stack.

Edited by Asmusr

Share this post


Link to post
Share on other sites
1 minute ago, Asmusr said:

Understood, but how do you get a value pushed onto the data stack that wasn't there before, like the number 65?

I will put an explanation in the CAMEL99 topic because I have derailed this one enough I think.

 

  • Like 1

Share this post


Link to post
Share on other sites
25 minutes ago, Asmusr said:

Edit: I assume BRANCH sets the IP to a value it pops from the data stack.

Yes

Share this post


Link to post
Share on other sites
Posted (edited)

 

32 minutes ago, TheBF said:

I will put an explanation in the CAMEL99 topic because I have derailed this one enough I think.

 

 

I think it does belong here, because it was discussed how GPL needs a quite long interpreter to decode and read the source operands before it jumps to the execution code. And Forth doesn't need that because all operands are already on the data stack. But what is the equivalent in Forth of 'load immediate' (LI in assembly, and I think it's ST or DST in GPL)? 

Edited by Asmusr
  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, Asmusr said:

Understood, but how do you get a value pushed onto the data stack that wasn't there before, like the number 65?

 

I can think of three ways to push numbers onto the stack:

  1. Typing a number at the console prompt.
  2. A number included in the definition of a word has LIT compiled just ahead of it, which, when executed, will push the number following it onto the data stack.
  3. The result of executing a word that pushes a number onto the data stack.

...lee

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, Asmusr said:

 

 

I think it does belong here, because it was discussed how GPL needs a quite long interpreter to decode and read the source operands before it jumps to the execution code. And Forth doesn't need that because all operands are already on the data stack. But what is the equivalent in Forth of 'load immediate' (LI in assembly, and I think it's ST or DST in GPL)? 

Ok. I put it over there and it is more detail than most want to see although this is a special group of people.

 

Technically there is not a LI in Forth and Forth is integer oriented not byte oriented.

 

There is store,  for convenience called !.    It is like poke in BASIC.

There is fetch, called @. It is like PEEK in BASIC.

 

 1234  HEX D000 !    ( put  integer 1234 into >D000)

 

HEX D000 @     ( fetch the contents of >D000 onto the data stack)

 

! is two 9900 instructions on a normal Forth, three 9900 instructions if top of stack is cached in a register.

@ can be one instruction if the Forth caches the top of stack in a register, other wise it is two.

Then after each instruction Lee's 3 instruction interpreter runs.

 

The operators for bytes are called C! and [email protected] (char store, char fetch)

 

To see how that plays out in real life:

VARIABLE X

VARIABLE Y

 

Variables just return an empty address onto the data stack. 

(The word VARIABLE is written in Forth meaning if you want a different kind of variable make it.)

 

1234 X !    ( x=1234)

X @ Y !     (  y=x )

 

Weird until you get used to it.

 

 

  • Like 1

Share this post


Link to post
Share on other sites
21 hours ago, RXB said:

 

QUOTE:

Of course, if it was stored in memory as BL @>1234, BL @>3456, etc... that would have the same effect but then it WOULD be compiled, since the result is executable. But it would also take twice as much memory to store. And like everything else in computing, you can easily cloud the issue by citing any of hundreds of examples of exceptions. ;)

 

So this is similar to XML commands as single byte commands in GPL right?

For that matter Assembly can also call these XML commands that are built in.

Yes, but this is NOT what happens, it was just an example I invented.

 

  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, TheBF said:

I will put an explanation in the CAMEL99 topic because I have derailed this one enough I think.

 

This thread started derailed. ;)

  • Like 1
  • Haha 3

Share this post


Link to post
Share on other sites

This is a good discussion which deserves its own thread.

  • Like 2

Share this post


Link to post
Share on other sites
19 hours ago, Tursi said:

This thread started derailed.

As @Tursi and the rest of us pretend this wasn’t completely entertaining watching everyone correct each other.

  • Like 2
  • Haha 1

Share this post


Link to post
Share on other sites
On 6/25/2021 at 6:35 PM, Jeff White said:

Reading the 99/4(A) patents gives additional insight on GPL.

 

How about a Forth written in GPL?

Visit uspto.gov to read patents 4,286,320, RE31,977, and related patents.

  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...