Jump to content

TheBF

+AtariAge Subscriber
  • Content Count

    3,166
  • Joined

  • Last visited

Posts posted by TheBF


  1. Getting back to Machine Forth, I was re-reading the original Machine Forth source code by Chuck Moore and realized that I did not implement branching and looping the way Chuck did it in his processor.  I had relied on work by Sam Falvo for the RISC V machine. Sam had used a JLE instruction for -IF  ( NOT IF) 

    Lee had mentioned that he wondered if that would work correctly. :)  

     

    Chuck's CPU had three branch instructions:

    • Un-conditional branch
    • Branch if TOS=false ( I use the EQ bit) 
    • Branch if Carry=false

    I missed that third one.  I in fact wrote it manually into the FOR NEXT loop structure to detect the zero crossing. DUH! 

    So here is how the newly organized branching and looping looks now.

    COMPILER
    \ compute signed offset & compile into the 2nd byte of any 9900 JUMP instruction
    : RESOLVE ( byte --)  2- 2/ SWAP 1+ C! ;
    
    : <BACK   ( addr addr' -- ) TUCK -  RESOLVE ;
    
    : ?JUMP   ( addr1 addr2)  0 JEQ, ; \ conditional jump
    : JUMP    ( addr1 addr2)  0 JMP, ; \ un-conditional jump
    
    \ Changed to match Chuck's original code and use Carry flag
    : -IF    ( -- $$)   THERE 0 JOC, ; \ goto THEN if Carry=TRUE
    : IF     ( -- $$)   THERE ?JUMP ;  \ goto THEN if EQ=TRUE
    : THEN   ( addr --) THERE OVER - RESOLVE ;
    : ELSE   ( -- $$ )  THERE JUMP  SWAP THEN ;
    
    : BEGIN    THERE ;  IMMEDIATE             \ THERE current dictionary address
    : WHILE     IF SWAP ;                     \ loop while EQ=false
    : -WHILE   -IF SWAP ;                     \ loop while Carry=false
    : AGAIN   ( addr --)  THERE  JUMP <BACK ; \ Jump back always
    : UNTIL   ( addr --)  THERE ?JUMP <BACK ; \ jump back until EQ=false
    : -UNTIL  ( addr --) -IF <BACK ;          \ jump back until Carry=false
    : REPEAT  ( addr -- ) AGAIN THEN ;
    

     

     

    So far the tests programs still run. 

     

    (The reason I got here was because I am trying learn how to use IF/THEN optimally in this weird Forth dialect for a screen driver)

     

    FOR/NEXT becomes 

    : FOR  ( n --)  I RPUSH,   I!  POSTPONE BEGIN ;
    : NEXT ( -- )   I1-!  -UNTIL  I RPOP, ;
    

     

    • Like 2

  2. 40 minutes ago, apersson850 said:

    The language and system depends on who delivers it.

    We have our own controllers, usually running under Linux and using some C-compiler for the software development.

    Then we have systems purchased from the outside. Today, many use ISO 61131 programming, where we most of the time write programs using Structured text. That's a control system language developed with influence from Pascal. Some of these also support C programming. Some have limited communcation capabilities and thus require some form of low level programming in the motor drives themselves.

    Some systems are outside of that "standard". We have some that are programmed in somethnig called Trio Motion BASIC. That's a parallel processing BASIC version, with deterministic timing in task execution and motion system commands. It's typically used for synchronizing several motors in a machine. There could be dozens of motors in the same machine, where some of them belong to groups that are linked together in more or less complex ways.

    Safety related systems are running on special, redundant hardware, where they execute special programs, written by combininig a finite number of function blocks with each other.

    Very nice.

    So I was thinking this might be related to PLCs. I guess with ISO 61131 it is a component of this work. It sounds like you have custom equipment as well.

    Are you involved in a specific industry with all this cool stuff?


  3.  

    3 hours ago, apersson850 said:

    So, that was just a detour to explain why my views occasionally aren't traditional in the hobby field. Be inspired if you like it, or disregard it if you find it non-applicable.

    Understood. 

    It certainly piques my interest.  Are these "machine control" programs written in P-code Pascal?

    I would think that an underlying interpreter like p-code would be ideal to abstract the endlessly changing hardware.

    If you are allowed to share, I would love to learn more. 

     

    Similar story from the Forth arena:

    This approach was taken my MPE in the UK (a Forth shop) when they got a contract for a European card terminal architecture. They created a byte coded VM that was a Forth VM under the hood.

    I think it had to be ported to existing hardware used all over Europe that ran on Z80, 68000 and I don't know what else.

    It is now an ISO standard I see:  https://www.iso.org/standard/54776.html

    They wrote a C compiler for it and you could also use their Forth toolchain. I suspect there would be other languages now if it still is in use.

    Since it's byte code I should think almost any appropriate language could be ported to it.

     

    So although Forth has had a tradition from the founder of endlessly looking for performance, when professionals use it is "sculpted" to be what makes sense for the application.

    Funny coincidence in that I was just demonstrating on another topic how simple it can be to add array index protection to a Forth array. :)

    Since you build your own data structures up from the ground you make them what you need. Failure to do so would be silly and/or outright dangerous depending on the application area.

     

     


  4. On 2/18/2022 at 8:48 AM, Willsy said:

    Yeah, that TYPE must be really fast! However, for a 'proper' implementation, you'll want to write TYPE in terms of EMIT, which may or may not invoke a screen scroll. EMIT is the bottleneck, as it has to maintain a notion of a current cursor position. And I was heartbroken at the impact of screen scrolling - it's as slow as hell. I don't think there's anything particularly wrong with TF screen scroll as invoked by EMIT, it reads/writes up to 80 bytes at a time. But boy is it slow! I have a little utility that allows one to read a DV80 text file and TF will compile it. Works a treat. Default action is to display the lines of code on the screen as it works through the text file. IIRC, the screen scrolling adds 300% overhead (in 80 column mode).

     

    In TF, you can disable screen scrolling with the SSCROLL variable - then it flies (the screen wraps, bottom to top).

    Yes it is fast and of course is only useful if you keep it on the screen as you say. I am still at the stage of trying to make the compiler generate correct code that is reasonably optimal. :) 

     

    I have to get all of my screen i/o lib working reliably to make a proper type as you describe but it is in progress. An yes SCROLLing sucks on the VDP chip.

    In the SEVENS benchmark Camel99 Forth does it in about 1:04. but over RS232 the same code runs in 26 seconds because the terminal is doing the scrolling. :)

    I like the top/bottom wrapping idea. I make use that for testing purposes. 

     

    • Like 1

  5. On 2/18/2022 at 7:59 AM, apersson850 said:

    Not sure if this is of any value to you, but in the UCSD p-system, when you write programs that declare constants, then the compiler creates a constant pool with these values. The pool is loaded into the heap when the program is to be executed. Then there are p-code instructions that fetch a constant from the constant pool to the stack. Of course you get the one-time overhead of creating the constant pool, but then you can push a word in the constant pool to the top of stack pretty easily:

    DECT SP
    MOV OFFSET(CONSTPOOL),*SP

    SP is the stack pointer register, CONSTPOOL the constant pool pointer and OFFSET the offset into the constant pool where you have the constant.

    If you want to move the constant pool in memory, you only need to update the pointer.

     

    Thank for taking an interest in this project.  Your background and experience far exceeds mine in this kind of thing. 

    This is a very good idea if we want to hold the constants in memory. It could become part of the project if I fly into a wall. :) 

     

    Since this little compiler is to generate a binary program image, I have, for better or worse, made constants generate no code unless invoked by name.

    So my "constant pool" is maintained in the compiler Forth's dictionary of words.

    This means constants must be declared after the "COMPILER" directive so they become part of the compiler's word set.

     

    Later when the constant is used the following code is emitted into the program:

    \ compiler code to put literal into top of stack register
                       SP DECT,
                 TOS   *SP MOV,  ( this is effectively "DUP" 
                 TOS <cons> LI, 
     

    So the constant is loaded as an immediate value into the TOS register.

    This is more verbose than normal because I have turned the 9900 into kind of Accumulator machine by dedicating R4 to hold the top of stack in a register.

    So we have to "push" the TOS before we use it for a new purpose.

    The two instructions that push the TOS register (ie: DUP)  have been changed into a PUSH/POP optimizer (called SMARTDUP) that removes those instructions if the TOS was previously "refilled" from memory and it also removes the refilling instruction ( *SP+ TOS MOV,) saving 6 bytes and lots of cycles.  

     

    So it is not as bad as it appears most of the time if the optimizer is turned-on.

    So far it works ... most of the time. :) 

     

     

     

     

     


  6. 4 hours ago, Willsy said:

    Oh, that's not nit-picking, Lee. That is an excellent point and well worth bringing up! Thanks for pointing it out! :thumbsup::thumbsup:

     

    Mark

    and it's an excellent example of what I am talking about , writing a FOXIT language to make things simple and reliable.

     

    So IMHO that kind of phrase should never be used if it occurs more than once. 

    99 MYBUFFER 5 + !

    but rather something like this should be made: (  [] used to remind the programmer that this in an array)

    : MYBUFFER[]    CELLS  MYBUFFER + ;
       99 5 MYBUFFER[] ! 

     

    And then GDMIKE can test this and be certain that it works as expected as a zero based array.

     

    And if we really want to be safe then he could add a line to protect index out of range and then remove it, if and when he thinks everything is safe.

    : ?INDEX   ( n -- n)  DUP   0 MAXINDEX  WITHIN 0= ABORT" Index range error" ;
    
    : MYBUFFER[]  
                ?INDEX
                CELLS  MYBUFFER + ;
    
    
    

     

    • Like 4

  7. I think I should port my slightly smaller SAMS colon/semi-colon to TF.

    That would save you some dictionary memory.

     

    But... If you have already used 28K of SAMS I suspect your code could benefit from some re-factoring to do more re-use of common pieces.

    In other words don't write the code in Forth, like the language is locked. (ie conventional)

    First create the language that you need to make a database and write in that.

    That is the secret of fitting lots of functionality into a Forth program. 

    • Like 1

  8. 3 hours ago, GDMike said:

    I need to equ a word to an address and reserve so much memory for it and I don't want my program to run it over.

    After you are done learning about conventional arrays, think about this idea.

     

    >> You don't have to assign space for data at the beginning or middle of the program code <<<

     

      After all your program has loaded into memory the word HERE points to the next available empty memory space.

     

    So imagine when your program starts it points your arrays and other data to use the empty memory at HERE and beyond.

    I will just leave it there for you noodle on. There are some things to watch out for.

    Just wanted to explain this concept that is not available in BASIC. (At least I don't think it is)

     

     

     

    (consult TF docs for where the "safe" end of memory really is) :) 

    _____________________________
    
    End of memory 
    
    -
    
    -
    
    All this is free to use.  
    -
    
    -
    
    Top of dictionary   <--- Called HERE 
    
          ^
    
          ^
    
          ^
    
    Bottom of dictionary (First word in dictionary) 
    
    _________________________________


     

    • Like 3

  9. MachForth Update

    (MachForth is a Forth compiler that generates executable binary programs that run native code, not threaded code) 

     

    Learning how to use a new kind of Forth has been challenging especially while writing it at the same time. :)

     

    I have gone back to working on interfacing to the VDP chip. This has lead me to see how much overhead happens when you run a stack machine on top of a register machine especially when you want a fast loop.  It can be done of course in Assembler but my goal has been to try and find a way to make MachForth source code compile code that is as efficient as possible.

     

    Chuck Moore added an extra register to his later Forth CPU designs that he called the 'A' register that is used to hold addresses that are used repeatedly. 

    I took R9 on the 9900 for this purpose.

     

    The VDP chip port addresses seem to be a perfect use case. With that in mind I have worked on the Forth word "TYPE".

    If I did with only Forth constructs it was pretty fast but it had noise words to handle the data stack inside the inner loop. 

    Using the A register idea gave me the tightest code.  (It makes me think I should find a way to use it in regular Forth)

    \ print string 
    : TYPE ( Caddr len -- )
        8C00 A!   \ load the address register with VDP port
        1- FOR
            *TOS->*AC!
        NEXT
        DROP
    ;
    

     

    Of course I needed to make that magic instruction for the inner loop.  Since the TOS register is where the action is in Forth, it made sense to add an instruction that moves bytes from the address in TOS to the address in the A register with auto-incrementing on the TOS  

     

    (The macro is created with the H:   host Forth's colon compiler not the TARGET compiler colon operator. )

    H: *TOS->*AC!  ( addr -- addr') *TOS+ *AR MOVB,  ;H

     

    The resulting code still has the overhead of a 9900 nested sub-routine call and FOR/NEXT loop setup, but the inner loop is pretty tight!

    For reference: Special register allocations

     

    R4  TOS  cache

    R6  DATA stack pointer

    R7  Return stack pointer

    R8  I register for loop indices

    R9  A register for addresses 

      2034  0647  dect R7            ( start FOR loop)
      2036  C5C8  mov  R8,*R7        ( save I register, the loop index)
      2038  C204  mov  R4,R8         ( load new loop index)
      203A  C136  mov  *R6+,R4       ( refill TOS from data stack with string address)
    
      203C  D674  movb *R4+,*R9      ( *TOS->*AC!  new instruction)
      203E  0608  dec  R8            ( NEXT loop )        
      2040  18FD  joc  >203c         
    
      2042  C237  mov  *R7+,R8       ( restore I register)       
      2044  C136  mov  *R6+,R4       ( DROP refills TOS from data stack         
    
      2046  C2F7  mov  *R7+,R11      ( restore R11 from return stack   
      2048  045B  b    *R11          ( return from sub-routine)         
    

     

     

     

     

     

    • Like 4

  10. I have been looking at SCAN ( addr len char )   a non-standard word but one that is in GForth and many other systems.

    It is a very handy primitive for finding a character in a string.

     

    I wondered how to do it in Forth and it really responds well to the dual WHILE structure because there are two loop ending conditions.

    It could be done with an AND as well I think but might need a more stack shuffles. 

    : SCAN (  adr len char -- adr' len')
            >R     \ remember char
            BEGIN
              DUP
            WHILE ( len<>0)
              OVER [email protected] [email protected] <>
            WHILE ( [email protected]<>char)
              1 /STRING            \ advance to next char address
            REPEAT
            THEN
            R> DROP  \ drop char   \ 32 bytes
    ;
    

    This made me realize I could do the dual WHILE trick in Forth Assembler now that I have the ANSI style loops as part of ASM9900.

     

    I am again impressed with the 9900 instruction set.  The point of making an interpreter is normally to save some space but the 9900 does the same function in 10 bytes less.

    So smaller and faster.  This is partly because of using registers rather than stack operations for the variables and also because the jumps in Assembler are short jumps and so take only 2 bytes per jump.  

    This make me wonder if I could use short jumps for Forth?  Hmmm...

    CODE SCAN  ( adr len char -- adr' len' )    \ find matching char
            TOS SWPB,           \ char stays in TOS
            2 (SP) W MOV,       \ address->w
            *SP+ R1 MOV,        \ POP count into R1,
            BEGIN,
              R1 R1 MOV,
            NE WHILE, ( len<>0)
                *W TOS CMPB,
            NE WHILE, ( *R8<>R1)
             ( do: 1 \STRING )
                 W INC,    \ inc. adr
                 R1 DEC,   \ dec. len
            REPEAT,
            ENDIF,
            W *SP  MOV,                    \ store updated address on stack
            R1 TOS MOV,                    \ updated count to TOS
            NEXT,  \ 26 bytes
            ENDCODE
    

     

    • Like 2

  11. 3 hours ago, Asmusr said:

    Yes and maybe. But 32bit x 16bit = 48bit.

    Yes 48 bits.

    I was thinking of "mixed" operations that Forth has.  There is a mixed multiplication operator (M*)  that does this but when I looked at the source for the one I have it is written in Forth.

    And although it makes use of the 9900 MPY to get a 32 bit result, applying the sign is rather involved, which I now get is why you want 32x32 "signed" multiplication in hardware. :) 

     

     

     

     


  12. 52 minutes ago, Asmusr said:

    One place where this could be relevant is for the F18A GPU. When I experimented with raycasting using the GPU, it would have been great to be able to multiply (signed) 32-bit numbers, and in order to access the extended RAM of the MKII, some instructions working on 32-bit addresses would also be useful. However, I don't think this is valuable enough to hold back the release of the MKII. 

    So you need 32bit X 32bit -> 64 bit result?

     

    Could you make do 32bit X 16bit -> 32bit result?


  13. 2 minutes ago, FarmerPotato said:

    I like the idea of expanding the Ts and Td fields. I guess the assembly would look the same but produce entirely different binary. 


    99105 builds 2 word opcodes on some unused 1-operand type instruction space. It uses the few bits to define a subopcode, whose real fields for src and dst are in the 2nd word.  I feel “yuck” when I read this. 
     


     

    Yup. Backwards compatibility is a straight-jacket.

    Oh. I forget to mention.  Give me two stacks as well! :) 

    • Like 2

  14. 1 minute ago, GDMike said:

    Ok. I'm getting this..I'll review a bit more, looks like something I really need.

    Well... I think TF can get you where you are going as the first pass.

     

    Then if you needed something to go much faster you re-write the bottleneck word, in Assembler -or- you could use this kind of thing.

    With Assembler and this compiler you don't want them in memory with your application cuz TI-99 so teeny.

    So you develop the faster word, convert to machine code and put the machine code version in your program.

     

    This compiler is my mental exercise for something I always thought was possible with Forth Assembler but didn't know how to do it exactly.

    I am happy that it is readable to you.

    It's just a bit different than normal Forth and you can put ASM code inline if your want to. :) 

     

    I replaced the source code for the compiler above.  Take a look at it, since you know ALC and you might get some insights to how your TF code actually works at the primitive level.

     

    • Like 3

  15. Hello world in ASM Forth becomes pretty understandable

    Spoiler
    \ hello world in ASM Forth. Running inside Camel99 Forth
    
    ONLY FORTH ALSO MFORTH
    MFORTH DEFINITIONS
    
    HEX
    8C02 CONSTANT VDPWA   \ Write Address port (absolute address so use EQU)
    8C00 CONSTANT VDPWD   \ Write Data port
    
    CREATE TXT  S" Hello World!" S,
    
    SUB: TYPE ( addr len) \ make a sub-routine because we can
        1- FOR   COUNT VDPWD #C!   NEXT DROP
    ;SUB
    
    CODE HELLO
         0 # VDPWA #C!     \ character store VDP address LSB
        40 # VDPWA #C!     \ character store VDP address MSB + "write" bit
        TXT #  COUNT TYPE  \ address -> tos
    ;CODE
    

     

     

    Classic99 QI399.046 2022-02-11 2_12_38 PM.png

    • Like 2

  16. 1 hour ago, FarmerPotato said:

    Imagine how you might go about extending the 9900 family to 32-bit. What problems do you run into and what solutions do you propose? 
     

    Yes, TI started over fresh with their 32-bit architectures like TMS320 and TMS340. (TMS430 is 16-bit again.) Except for 340, these are still going strong, alongside TI’s 32-bit ARM offerings. 

    A definition of 32-bit will include 32-bit internal data path, really everything wider like registers, address space, arithmetic. 

     

    I guess this 9900 would be designed in the 386 era? 
     

    It must be 9995 compatible, and 99105 if you’re familiar with that.  99105 does have some 32-bit arithmetic and some 2 word opcodes.

     

    Note: many 32-bit architectures pack two 16-bit instructions into a 32-bit word. 
     

    It would really benefit from a clean slate as far as the instruction set goes IMHO.

     

    By that I mean opening up some of the bit fields to allow 32 registers in a workspace.

    Post inc. and post dec. would be nice. ( *R1+  *R1- ) 

     

    Of course there goes compatibility.

     

     

    • Like 2

  17. ASMForth is the simplest Machine Forth I can think of that still uses the features of the 9900.

    At it's simplest it literally renames some instructions to Forth vernacular.

    There is a bit of a challlenge with literal numbers and addresses. My cheap and dirty solution is to preface some Forth operators with the # symbol.

    This indicates a literal number or address is the required parameter.

     

    I have added nestable sub-routines and a nestable FOR/NEXT loop just to show how easy it is.

    It does not have tail-call optimization like MACHFORTH or any other form of optimization.

    It's an Assembler that uses Forth style mnemonics. 

     

    Willsy had expressed some interest in what I was doing here so this could be easily ported to another Forth I think.

    EDIT: Updated with some bug fixes 

    Spoiler
    \ Experimental 9900 Assembler using Forth syntax elements  Sept 11, 2021
    
    \ Syntax conventions:
    \ 1. Words that end with a comma are raw 9900 instructions
    \    They use 9900 parameters
    \ 2. Forth words assume TOS is in R4. You DUP and DROP as needed.
    \ 3. Store ! +! 1+! 2+! 1-! 2-!  use symbolic addressing with variables
    
    \ /////////// REGISTER USAGE \\\\\\\\\\\
    \ R0   temp
    \ R1   temp
    \ R2   temp
    \ AR   R3  is a general purpose address register
    \ TOS  R4 is top of stack cache (you need to manage it)
    \ R5   temp
    \ SP   data stack pointer
    \ RP   return stack pointer
    \ R10  holds NEXT address
    \ R11  sub-routine linkage
    \ R12  CRU I/O
    \ R13  for BWLP
    \ R14  for BWLP
    \ R15  for BWLP
    
    
    \ NEEDS DUMP FROM DSK1.TOOLS
    \ NEEDS MARKER FROM DSK1.MARKER
    \ NEEDS WORDSLIST FROM DSK1.WORDLISTS
    INCLUDE DSK1.SUPERTOOLS \ this gets all the above files into Cartidge Space
    
    
    ONLY FORTH DEFINITIONS
    VOCABULARY MFORTH
    
    HERE
    ONLY FORTH ALSO ASSEMBLER ALSO MFORTH
    
    MFORTH DEFINITIONS
    DECIMAL
    : ?REGARG ( n --) 16 0  WITHIN ABORT" Register expected" ;
    HEX
    : ?ADDR   ( n --) 2000 < ABORT" Invalid memory address" ;
    
    \
    \ Register names
    \ Unlike normal Forth we can get at some registers directly by name with MOV,
    \   NOS TOS MOV,
    \ : OVER ( a b -- a b a)  DUP  3RD TOS MOV, ;
    \ : 2PICK ( a b c -- a b c c)  DUP  4TH TOS MOV, ;
    
    \ Named stack items in memory...
    : NOS     *SP  ;    \ Next on Stack
    : NOS+    *SP+ ;    \ pop next on stack to another register
    : 3RD   2 (SP)  ;
    : 4TH   3 (SP)  ;
    : 5TH   4 (SP)  ;
    
    3 CONSTANT AR  ( Chuck's 'A' register,  "address register")
    : (AR)   AR () ;
    : *AR    AR ** ;
    : *AR+   AR *+ ;
    
    CR .( Forth Intrinics)
    \ These inline code generators are in the actual compiler
    HEX
    \  Moore's Machine Forth + a few extras
    : DROP  ( n -- ) NOS+ TOS MOV,  ;
    : DUP            SP DECT,  TOS NOS MOV, ;
    
    .( .)
    : 2*     ( n -- n ) TOS TOS ADD, ;
    : 2/     ( n -- n)  TOS 1 SRA, ;
    : INVERT ( n -- )   TOS INV, ;
    : AND    ( n mask -- n) NOS INV,  NOS+ TOS SZC, ;
    : MASK   ( n n -- n ) TOS SWAP ANDI, ;  \ AND TOS with literal number
    : XOR    ( n n -- n)  NOS+ TOS XOR, ;
    : +      ( n n -- n)  NOS+ TOS ADD, ;   \ (option 1) add on stack
    : #+     ( n n -- )    TOS SWAP AI, ;   \ (option 2) TOS + literal number
    
    \ ___________________________________________________________________
    CR .( A register. C. Moore idea for Machine Forth )
    \ We used the features of the TMS9900 CPU. It's dumb not to.
    
    \ These Forth conventions are kept: @ means fetch and ! means store.
    \ '*' suffix means indirect memory access like TI Assembler
    
    \  Fetch to TOS always saves TOS before operation
    : [email protected]    ( -- n) DUP   AR  TOS MOV, ;  \ Dpush(T) T=A
    : *[email protected]   ( -- n) DUP  *AR  TOS MOV, ;  \ Dpush(T) T=*A
    : *[email protected]+  ( -- n) DUP  *AR+ TOS MOV, ;  \ Dpush(T) T=*A  A=A+cell
    
    \ Store from TOS always refills TOS after operation
    : A!    ( addr -- ) TOS  AR  MOV, DROP ;  \ A=T  Dpop(T)
    : A+!   ( n -- )    TOS  AR  ADD, DROP ;
    : *A!   ( addr)     TOS *AR  MOV, DROP ;  \ [A]=T Dpop(T)
    : *A!+  ( n --)     TOS *AR+ MOV, DROP ;  \ [A]=T A=A+cell Dpop(T)
    
    \ indexed addressing fetch/store on A register to/from TOS
    : (A)@  ( u --)   DUP  (AR) TOS MOV, ;    \ Dpush(T) [email protected](A)
    : (A)!  ( n --)   TOS  SWAP (AR) MOV, DROP ;
    
    \ Using more 9900 instructions on A register
    : A1+!  ( -- )    AR INC,  ;
    : A2+!  ( -- )    AR INCT, ;
    : A1-!  ( -- )    AR DEC,  ;
    : A2-!  ( -- )    AR DECT, ;
    
    \ byte operations manage reversing the byte in the in TOS register
    : *[email protected]   ( -- 0c00)  DUP  *AR  TOS MOVB, TOS 8 SRL, ;
    : *[email protected]+  ( -- 0c00)  DUP  *AR+ TOS MOVB, TOS 8 SRL, ;
    : *AC!   ( 0c00 --)  1 (TOS) *AR  MOVB,  DROP ;
    : *AC!+  ( 0c00 --)  1 (TOS) *AR+ MOVB,  DROP ;
    
    .(  return stack operators .)
    \ Chuck Moore's CMForth used PUSH & POP for rstack
    \ BF kept them familiar
    : DUP>R ( n -- n) TOS RPUSH, ;
    : >R    ( n -- )  DUP>R DROP ;
    : R>    ( -- n)   DUP  TOS POP, ;
    
    : [email protected]    ( -- n )  DUP  *RP TOS MOV, ;
    : R!    ( n -- )  TOS  *RP MOV,  DROP ;
    : R!    ( n --)  TOS *RP MOV,  DROP ;
    : R+!   ( n --)  TOS *RP ADD, DROP ;
    
    \ /\/\/\/\/\/\/\[ Chuck Moore Machine Forth Ends ]/\/\/\/\/\/\/\/\
    
    \ no-brainer 9900 optimizations
    : R1-! ( -- )    *RP DEC, ;
    : RDROP ( -- )    RP INCT, ;
    
    : 1+     TOS INC, ;
    : 2+     TOS INCT, ;
    : 1-     TOS DEC, ;
    : 2-     TOS DECT, ;
    : 4*   ( reg -- n') TOS  2 SLA, ;
    : 8*   ( reg -- n') TOS  3 SLA, ;
    
    : LSHIFT ( n -- n)  TOS SWAP SLA, ;
    : RSHIFT ( n -- n)  TOS SWAP SRL, ;
    
    \ inc/dec variables with symbolic addressing
    : 1+! ( addr -- )  @@ INC, ;
    : 2+! ( addr -- )  @@ INCT, ;
    : 1-! ( addr -- )  @@ DEC, ;
    : 2-! ( addr -- )  @@ DECT, ;
    
    : +!   ( n addr --) TOS SWAP @@ ADD,  DROP ; \ 6 bytes  \ 5 # X +!
    
    : NOP    ( -- )     0 JMP, ;
    : ABS    ( n -- n') TOS ABS, ;
    : NEGATE ( n --n')  TOS NEG, ;
    : OR     ( w w -- w ) NOS+ TOS SOC, ;
    : #OR    ( -- )    TOS SWAP ORI, ;
    
    \  2 and 3 instruction words for ANS/ISO compatibility
    : -     ( n n -- n') NOS+ TOS SUB, TOS NEG, ;
    : UM*   ( n n -- d)  NOS  TOS MPY, R5  NOS MOV, ;
    : *     ( n n -- n)  NOS+ R3 MOV,  TOS R3 MPY, ;
    
    : ALIGNED ( n -- n)   TOS INC,  TOS -2 ANDI, ;
    : AND   ( w w -- w ) NOS INV,  NOS+ TOS SZC, ;
    : [email protected]    ( addr -- d) 2 (TOS) PUSH,   *TOS TOS MOV, ; \ fetch double integer
    : ON        ( variable --) @@ SETO, ;
    : OFF       ( variable --) @@ CLR,  ;
    : ><        ( n -- )  TOS SWPB, ;
    
    CR .( MFORTH jump tokens)
    HEX                  \ Action if TRUE
     01 CONSTANT >       \ JLT to ENDIF, *signed
     02 CONSTANT U>      \ JLE to ENDIF,
     03 CONSTANT 0<>     \ JEQ to ENDIF,
     04 CONSTANT U<      \ JHE to ENDIF,
     05 CONSTANT <=      \ JGT to ENDIF, *signed
     06 CONSTANT 0=      \ JNE to ENDIF,
     07 CONSTANT .OC.    \ JNC to ENDIF,
     08 CONSTANT .NC.    \ JOC to ENDIF,
     09 CONSTANT .OO.    \ JNO to ENDIF,
     0A CONSTANT U<      \ JLO to ENDIF,
     0B CONSTANT U>=     \ JH  to ENDIF,
     0C CONSTANT .NP.    \ JOP to ENDIF,
    
    CR .( branching and looping)
    : IF     ( addr token -- 'jmp') IF, ;
    : ENDIF  ( 'jmp addr --)  ENDIF, ;
    : ELSE   ( -- addr ) ELSE, ;
    
    : BEGIN  ( -- addr)  HERE ;
    : WHILE  ( token -- *while *begin) WHILE, ;
    : AGAIN  ( *begin --) AGAIN,  ;
    : UNTIL  ( *begin token --) UNTIL, ;
    : REPEAT ( *while *begin -- ) REPEAT, ;
    
    CR .( memory fetch/store)
    : @     ( TOSaddr -- n) *TOS TOS MOV, ;
    : #@    ( addr -- n) TOS PUSH, ( addr) @@ TOS MOV, ; \ fetch from literal address
    
    \ added byte operations: BFox
    : #[email protected]   ( addr -- c)  TOS PUSH, ( addr) TOS MOVB,  TOS 8 SRL, ;
    : [email protected]    ( addr -- c) *TOS TOS MOVB,    TOS 8 SRL, ;
    
    : !      ( n addr --)  NOS+ *TOS MOV, DROP ;
    : #!     ( n [variable] -- ) TOS SWAP @@ MOV, DROP ;
    
    : TO    ( reg -> variable)  ' >BODY  ! ; \ register to variable
    : LD#   ( n REG -- )  SWAP ?REGARG LI, ; \ n -> REG
    : #     ( n -- )   TOS PUSH,  TOS SWAP LI, ; \ literal to TOS
    
    HEX
    : ->   ( var1 <var2> --) \ X -> Y   1 instruction 6 bytes
           ' >BODY   \ find CFA(var2), convert to data field
            SWAP  @@  ROT @@  MOV, ;  \ assign var1 to var2
    
    \ Writes code to swaps byte in register automatically
    : C!     ( c  addr -- )   NOS SWPB,   NOS+ *TOS    MOVB, DROP ;
    : #C!    ( REG [addr] --) TOS SWPB,   TOS  SWAP @@ MOVB, DROP ;
    
    \ ********* ANS/ISO Forth, Stack operators ****************
    : OVER  ( n1 n2 -- n1 n2 n1)  DUP  3RD TOS MOV, ;
    : NIP   ( n1 n2 -- n2)  SP INCT, ;
    : SWAP  ( n1 n2 -- n2 n1)  TOS R0 MOV,  NOS TOS MOV,  R0 NOS MOV, ;
    
    \ ////////////////////////////////////////////////////////
    \ --------------  higher level constructs  --------------
    \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
    : BOUNDS ( adr len -- adr2 adr1) NOS R1 MOV, TOS NOS ADD, R1 TOS MOV, ;
    : NOT       ( -- )  R1 STST,  R1 2000 ANDI, ;  \ invert EQ flag status
    : ?TERMINAL ( -- ?) 0020 @@ BL,  NOT ;
    
    \ CUT n characters from left side of STRING (addr,len)
    : /STRING ( addr len n -- addr' len' ) TOS NOS SUB,  TOS 2 (SP) ADD,  DROP ;
    
    \ ** WARNING ** puts the string address in register A
    : [email protected]   ( Caddr --  len) ( A: Caddr+1)
            A!     \ base address to register A
            A1+!   \ bump address past count byte
           *[email protected]    \ fetch byte count onto data stack
    ;
    
    CR .( sub-routine creator)
    \ subs call themselves when invoked by compiling a BL instruction
    : SUB:
    \ compile time action
       CREATE  !CSP
               R11 RPUSH,    \ compile "enter sub-routine"
                      \ <--- your program code compiles here
       DOES> ( sub-addr) @@ BL, ; \ Runtime: compile BL to this sub-routine
    
    : ;SUB    ?CSP  R11 RPOP,  RT, ;  \ compile exit sub-routine code
    
    : ;CODE    *R10 B,   ENDCODE ;
    
    CR .( FOR/NEXT)
    \ Simple FOR/NEXT loop written in ASM Forth.
    \ Uses return stack for index because registers are in short supply.
    \ Speed reduction is ~11% on 9900 VS index in a register.
    \ Use  [email protected]  as a loop index.  Use  nn R+!  to STEP down faster.
    \ Use TOS or A register if you need an up-counting register
    : FOR  ( n --)  >R  BEGIN ;
    : NEXT ( -- )   R1-! .NC. UNTIL  RDROP ;
    
    : COUNT  ( Caddr -- addr len )
            DUP
            NOS INC,
           *TOS TOS MOVB,
            TOS 8 SRL, ;
    
    HERE SWAP - DECIMAL .  .( bytes)
    

     

     

    Some simple test code

     

    Spoiler
    \ MFORTH Assembler tests
    
    ONLY FORTH DEFINITIONS
    ALSO ASSEMBLER \ Compiler understands ASM words
    
    VARIABLE X
    VARIABLE Y
    VARIABLE Z
    
    \ Code in Forth Assembler
    HEX
    CODE ASM1
           TOS PUSH,      \ make space in the TOS register (R4)
           TOS FFFF LI,   \ literal number to TOS
           BEGIN,
             TOS DEC,
           EQ UNTIL,       \ !!! does not consume the top of stack
           TOS POP,        \ restore old TOS from data stack
           NEXT,
    ENDCODE
    
    \ Same program in MForth Assembler.
    ONLY FORTH ALSO MFORTH
    MFORTH DEFINITIONS
    HEX
    CODE MFTEST1
           FFFF #        \ literal number to TOS (# makes space in TOS)
           BEGIN
              1-
           0= UNTIL      \ !!! does not consume the top of stack
           DROP          \ restore old TOS from data stack
    ;CODE
    
    \ using variables/addresses
    CODE MFTEST2
           FFFF #
           BEGIN
              1-
              DUP X !   \ Store uses ONLY symbolic addressing
              X -> Y    \ mem2mem  X->X assignment
              Y -> Z    \ Y -> X
           0= UNTIL
           DROP
    ;CODE
    \ emitted code for Mforth2
    \   CADA  0644  dect R4                     (14)
    \   CADC  C584  mov  R4,*R6                 (30)
    \   CADE  0204  li   R4,>ffff               (20)
    \   CAE2  0604  dec  R4
    \   CAE4  C804  mov  R4,@>ca86
    \   CAE8  C820  mov  @>ca86,@>ca90
    \   CAEE  C820  mov  @>ca90,@>ca9a
    \   CAF4  16F6  jne  >cae2
    \   CAF6  045A  b    *R10
    
    CODE MFTEST3  \ counter using A register
          FFFF #
          BEGIN
             A2+!
             1-
          0= UNTIL
          DROP
    ;CODE
    
    CODE MFTEST4
         FFFF # FOR  A1+!  NEXT  ;CODE
    
    \ \\\\\\\\\\\\\  sub-routines /////////////
    SUB: SUB1
        Y #@
        FOR
            [email protected] X !
        NEXT
    ;SUB
    
    SUB: SUB2
        10 # Y !
        SUB1
        Z 1+!
    ;SUB
    
    CODE MAIN
         1000 #
         FOR
           SUB2
           20 # Z +!
         NEXT
    ;CODE
    

     

     

    • Like 3

  18. 8 minutes ago, humeur said:

    I am looking for intensity  in Ampere       No power          power is    P=U*I

     

    electromotive    in french is  électromotrice  it's more for engines     is not Ampere

     

     

    En Anglais on dit   "current", measurer en Ampere. 

    • Like 1
    • Thanks 1
×
×
  • Create New...