-
Content Count
3,166 -
Joined
-
Last visited
Posts posted by TheBF
-
-
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, ;
-
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 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.
-
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.
-
1
-
-
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.
-
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!
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 + ;
-
4
-
-
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.
-
1
-
-
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) _________________________________
-
3
-
-
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)
-
4
-
-
And I am a Fox.🤣
-
1
-
-
This one is free, in PDF, written by a man I can say I know on first name basis.
-
1
-
1
-
-
It certainly can. I have been lucky over the years and got what I ordered.
Not sure if you use Amazon but this book is based on the courses taught at Forth Inc.
-
2
-
-
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
-
2
-
-
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.
-
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?
-
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!
-
2
-
-
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.
-
3
-
-
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
-
2
-
-
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.
-
2
-
-
9 hours ago, GDMike said:Making time for documentation today
Is that user Docs or programmer Docs or both?
-
1
-
-
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
-
3
-
-
Just now, humeur said:A mon tour " je vous en pris" French is not easy.
Jean Louis.
Like I said.
Merci.
-
1
-
-
2 hours ago, humeur said:Je sais que je suis pas doué en anglais.
merci pour la traduction
Je vous empris M.
(et je ne suis pas tellement fluent en Francais)
-
1
-
-
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.
-
1
-
1
-
fbForth—TI Forth with File-based Block I/O [Post #1 UPDATED: 04/13/2021]
in TI-99/4A Development
Posted
I have no direct knowledge but given that it's a 42 (?) year old design my money is on, it's not changing.
It's just a feeling mind you.
(There's always a smartass)