Search the Community
Showing results for tags 'fbforth'.
Found 2 results
This thread is a discussion of the evolution of fbForth, my file-based implementation of TI Forth. Though I have replaced the sector-based block I/O of TI Forth with a file-based version for fbForth, I have written utilities to browse TI Forth disks and to copy TI Forth blocks to fbForth blocks files (see latest FBLOCKS file far below). Several obvious advantages to using file I/O over sector I/O: It is simple to make a system disk of any larger size and density by just copying the system files to the new (usually larger) disk. If there is filespace on the system disk, you can save other useful programs there, such as CorComp's Disk Manager, without fear of corrupting the system disk. There is no danger of corrupting a disk by saving Forth blocks---they are only stored to the current blocks file. One can use disks of different sizes with impunity. The only significant disadvantage I can think of is not being able to use the new system with the old. The viewer/copier utilities, which I mentioned above and included in the latest FBLOCKS file posted far below, render that merely an inconvenience. There is also a slight performance penalty to reading 8 records one at a time instead of 4 sectors all at once for each block read. This is somewhat mitigated by saving 896 bytes of VRAM. I am certainly open to any and all suggestions. ...lee ---------------------------------------------------------------------- fbForth 2.0: A File-Based Cartridge Implementation of TI Forth is now published as a book available for $14.99 on Amazon.com. It is also on Amazon Europe for various similar prices: fbForth now has a website of its own: fbforth.stewkitt.com fbForth downloads & cartridge (fbForth 2.0)— fbForth 1.0 (E/A3 executable): Executable and FBLOCKS—fbForth100b.zip Manual—fbForth_1.0_Manual_a.pdf fbForth 2.0:11 (32KB ROM cartridge) [NOTE: Contains new Font Editor]: 80-Column option on TI-startup screen. Bug fixes and the handful of words added to the resident dictionary are listed in posts #1067, #1116, #1170, #1173, #1426 and #1441 among others. The following additonal words have also been added to the resident dictionary: BYE SD0 CMOVE> ABORT" DATA[ ]DATA DCHAR SPDCHAR CODE: ;CODE WRAP PANEL SCROLL TALKING? SAY STREAM SOUND PLAY PLAYING? S0&TIB! SAMS? SAMS! >MAP \ N>S DOES>CODE: S|F SM/REM FM/MOD ■■■CARTRIDGE available■■■ ($30 + S&H)—PM me if you want me to build you one. fbForth 2.0 labels (as on the above cartridge image) at $0.50 each + S&H (1st class stamp)—PM me if interested. Four 8KiB Inverted Bank Binaries + Batch File (packs binaries, inverted or normal, into 32KiB, 64KiB, 128KiB, 256KiB and 512KiB images)— fbForth 2.0:7—fbForthBinPack.zip fbForth 2.0:8—fbForthBinPack_20160529.zip fbForth 2.0:9—fbForthBinPack_20160924.zip fbForth 2.0:10—fbForthBinPack_20171019.zip fbForth 2.0:11—fbForthBinPack_20181212.zip Classic99 & Real Iron 32KiB Inverted Binary (contains latest FBLOCKS and fbForthBinPack ZIPs) [ NOTE: In Classic99.ini, the rom0 line should be: rom0=3|0000|8000|MODS\fbForth200_9.bin ]— fbForth 2.0:3—fbForth200_20151026.zip fbForth 2.0:4—fbForth200_20151109.zip fbForth 2.0:5—fbForth200_20151208.zip fbForth 2.0:7—fbForth2.0-7_Cartridge_20160412.zip fbForth 2.0:8—fbForth200_20160529.zip fbForth 2.0:9—fbForth200_20160924.zip fbForth 2.0:10—fbForth200_20171019.zip fbForth 2.0:11—fbForth200_20181212.zip MESS 32KiB Inverted Binary in RPK file (contains latest FBLOCKS and fbForthBinPack ZIPs)— fbForth 2.0:3—fbForth200_MESS_20151026.zip fbForth 2.0:4—fbForth200_MESS_20151109.zip fbForth 2.0:5—fbForth200_MESS_20151208.zip fbForth 2.0:7—fbForth2.0-7_Cartridge_MESS_20160412.zip fbForth 2.0:8—fbForth200_MESS_20160529.zip fbForth 2.0:9—fbForth200_MESS_20160924.zip fbForth 2.0:10—fbForth200_MESS_20171019.zip fbForth 2.0:11—fbForth200_MESS_20181212.zip Manual—fbForth 2.0: A File-Based Cartridge Implementation of TI Forth— [August 18, 2017 Book Publication]—See top of post for more information [April 28, 2017 Update]—fbForth_2.0_Manual_20170428.pdf [June 20, 2015 Update]—fbForth_2.0_Manual_20150620.pdf Addendum 2.0:8— fbForth_2.0_Manual_Addendum.pdf fbForth_2.0_Manual_Addendum_20160620.pdf Previous FBLOCKS files— FBLOCKS_20151102.zip FBLOCKS_20151109.zip FBLOCKS_20151117.zip FBLOCKS_20151130.zip FBLOCKS_20151204.zip FBLOCKS_20151208.zip FBLOCKS_20160406.zip FBLOCKS_20160412.zip FBLOCKS_20160529.zip FBLOCKS_20160617.zip FBLOCKS_20160620.zip FBLOCKS_20160924.zip FBLOCKS_20170419.zip FBLOCKS_20171019.zip FBLOCKS_20171110.zip Latest FBLOCKS file—(FIAD file; 90KiB, 360KiB and 400KiB (CF7+/nanoPEB) images; as well as a few font files)—FBLOCKS_20181212.zip, which includes CFPMOUNT—This addition to the Compact Flash Utilities persists the mounted volume even after system reset Block #1 (Welcome Screen) modifications: Allows changing text colors to White on Blue Displays SAMS availability Bytes of free Low Memory (Return Stack grows down in this space from R0 ) Bytes of free High Memory (Dictionary grows up from HERE ; Stack grows down from S0) BLK>FILE—Current Blocks File to DV80 File export utility—ported from TurboForth with permission from Mark Wills FILE>BLK—DV80 File to Current Blocks File import utility—ported from TurboForth with permission from Mark Wills Compact Flash Utilities—Allow mounting of CF virtual disks in nanoPEB/CF7+ devices CF?—Leaves TRUE flag if CF&+/nanoPEB present; FALSE otherwise CFVOLS—Leaves volume #s associated with DSK1, DSK2 and DSK3 CFMOUNT—Temporarily (until system reset) mounts a volume in DSK1, DSK2 or DSK3 ASM>CODE—Outputs Machine Code in CODE: ... ;CODE format to a file in Append mode CPYBLK—Block Copying Utility—[fixed bug that would copy blocks from wrong file if they were already in block buffers and had corresponding block numbers] CRU Words—rewritten with CODE: ... ;CODE 64-Column Editor Memory Dump Utility [Note: VLIST is now part of the resident dictionary] TRACE—Colon Definition Tracing Printing Routines TMS9900 Assembler Disk Catalog Utility, DIR (ported from TurboForth code with @Willsy’s permission Disk Catalog Utility, CAT (reads VIB, FDIR and FDRs to extract information) More Useful Stack Words etc. (most required by the String Library) Portable String Library (ported by @Willsy and slightly modified by Yours Truly) TI Forth Block Utilities (includes a windowed viewer/copier)—fixed EMIT8 bug Compact Flash Utilities for mounting virtual disks in the nanoPEB/CF7+ In addition to the above, there are much-faster-loading binary images of the 64-Column Editor TMS9900 Assembler Portable String Library Note: The binary images in the last 3 menu options will only work with the revision of fbForth with which they were BSAVEd. The current revision is fbForth 2.0:11 and will work with the binary images in FBLOCKS_20171205.zip and later (until the next revision).
Forth: The Cart Before the Horse (#5) This is one is going to be brief. If you've read any of the Forth primers, or any of the Forth proclamations that I and others make here on Atariage from time-to-time, you'll no doubt have read about how versatile and configurable the language is. We're going to have a very brief look at that today. But it won't be the War And Peace tome that I wrote yesterday. Brian Fox recently wrote some fascinating code for Camel99 that gives the language a more Basic-like syntax. As you probably know, in Forth, arguments and parameters to words (functions) come *before* the word/function that you want to call. This is because words/functions take their data, and put their data on the stack, so the stack has to be loaded before the word is called: TI BASIC: CALL HCHAR(ROW,COLUMN,CHAR,REPEATS) Forth: ROW COLUMN CHAR REPEATS HCHAR This tends to put people off when they look at Forth. It just looks like gobbledy-gook; at least until you understand that ROW and COLUMN etc are going on the stack, and HCHAR removes them. However, Forth is supposedly "the most flexible language of them all", "ultra malleable, "if you don't like it you can change it" blah blah blah. So, why don't we put our money where our mouth is and prove it? Well alrighty then! Inspired by Brian's look at HCHAR and VCHAR I thought it might be fun to demonstrate how the language can be changed to suit your preferences. There are some restrictions, sure, but I think you'll be impressed. We're going to change Forth's HCHAR and VCHAR into TI BASIC's HCHAR and VCHAR, where the arguments come after the word. So, again, let's recap: TI BASIC: CALL HCHAR(ROW,COLUMN,CHAR,REPEATS) Forth: ROW COLUMN CHAR REPEATS HCHAR We're going to end up with: CALL HCHAR( ROW COLUMN CHAR REPEATS ) Furthermore, we want it to be sophisticated enough such that the parameters can be numbers (called literals in Forth parlance) or calls to other words, or complex Forth expressions that compute a value etc. What might shock you is how much code is NOT required to do this. I give you: : CALL ( -- ) ; IMMEDIATE \ do absolutely nothing : HCHAR( ASCII ) WORD EVALUATE STATE @ IF COMPILE HCHAR ELSE HCHAR THEN ; IMMEDIATE : VCHAR( ASCII ) WORD EVALUATE STATE @ IF COMPILE VCHAR ELSE VCHAR THEN ; IMMEDIATE Now, you can type this directly on the command line: PAGE CALL HCHAR( 10 10 42 10 ) CALL VCHAR( 10 10 42 10 ) And behold the awesomeness. Note the spaces: The space between the open parenthesis of HCHAR( and VCHAR( is essential. The spaces between the parameters are just normal for Forth (and actually looks much nicer than using commas). The space before the final closing parenthesis is also required. You just changed Forth to be more like BASIC. You don't have to have numbers in the parameter list. They can be words or expressions: 10 CONSTANT TEN 42 CONSTANT FORTY-TWO CALL HCHAR( TEN TEN FORTY-TWO TEN TEN + ) (That last phrase: TEN TEN + puts 10 on the stack, then another 10 on the stack, then + ("add") removes them and replaces them with their sum, thus leaving the repeat count for HCHAR) So how does it work? Let's look at CALL first. CALL does nothing. It's only job is to be there to make those more familiar with BASIC happy. It has no code in it; it's empty. Furthermore, it's what is known as an IMMEDIATE word, meaning that it executes DURING COMPILATION, not during execution. An example: : FRED ( -- ) CR ." I AM FRED" CR ; Type that in. Nothing much happens. The word FRED gets stored in the dictionary ready to be used. Now type FRED and press enter. FRED executes. No big deal. Now, type this: : BOB ( -- ) CR ." BOO! BOB WAS HERE!" CR ; IMMEDIATE Okay, you typed it in. Nothing much happened. Now execute it: type BOB and press enter. Again, no big surprises. Now, try this: : TOM ( -- ) BOB CR ." HELLO! I AM TOM!" CR ; Did you see what happened? While *TOM* was being compiled, BOB got in on the act and ran, rather rudely announcing his presence. So what happens if we run TOM (type TOM and press enter)? HELLO! I AM TOM! Where's BOB? Should BOB not also say BOO!? No. And the reason why is very clever and is the secret sauce that makes Forth so very powerful. Here it is: "Immediate words execute at compile time." That is, immediate words execute when a word is being compiled, *not* when the compiled word is executed! That's possibly a brain-hurting statement. Consider this TI BASIC code: 10 CALL HCHAR(10,10,42,10) Now, when you press enter, the TI BASIC compiler switches on and compiles your code into some internal magic code that will do what you want it to do when you later RUN it. Okay. All normal stuff. But consider this: When the TI BASIC compiler is compiling that line of code, it does so entirely privately. No have no control over what compiler does. Mind your own business, it's nothing to do with you. The compiler privately compiles that code (or doesn't if there's an error), and you are just a bystander. That's not the case in Forth. In Forth, you can use "immediate" words that run when the compiler is compiling. And because they run when the compiler is compiling, you can "hijack" the compiling process, and do something: make a fart sound; say something on the speech synth; load a file; anything you want. You can even compile your own code. Think about that. Code that compiles code. And THAT is what makes Forth so powerful. So, lets get back to TOM and examine what happened. In Forth, the compiler is switched on by : (colon) and switched off by ; (semi-colon): : SOME-WORD <CODE GOES HERE> ; The compiler just walks along the line of text, and when it sees a word it looks for it in its dictionary and if it finds it, it compiles a call (like a GOSUB) to it. Now you can see why spaces are so critical in Forth. They are what separate the words so that they can be found in the dictionary. However, when the compiler is looking for a word, if it finds it, it checks to see if it is immediate or not. If it is not, it just compiles a call/GOSUB to the word. However, if it *is* immediate, it *executes* it, and does not compile it. That means you can put a reference to an immediate word in your definition, and at that point in the compilation process it will call your immediate word, and *you* can do something to the word that is *currently* being compiled, like add some more code to it. When the immediate word ends, the compiler just carries on compiling, totally oblivious to anything you may or may not have done to the word currently being compiled. It's none of its business. It's your business. You are in total control. Thus when TOM was being *compiled* the compiler saw the reference to the word BOB and saw that it was "an immediate word" and so it executed BOB, and BOB did it's thing (in this case, writing a cheeky message to the screen) and then carried on with the compiling. Now you understand why, when TOM was *executed*, there was no message from BOB. BOB did it's thing while TOM was being *compiled*. Yes. In Forth, there are two distinct excecution phases: Run-time: When a word is just plain excecuting, doing its thang; Compile time: When a word is being compiled. And you can do whatever the hell you want in either phase. You might want to go for a little lie down at this point! Now, lets look at HCHAR( and see what it does: : HCHAR( ASCII ) WORD EVALUATE STATE @ IF COMPILE HCHAR ELSE HCHAR THEN ; IMMEDIATE When the compiler sees HCHAR( it sees that it is immediate and so it executes it. The first thing is does is place the ASCII code for a ) (closed parenthesis) on the stack. Then WORD executes. WORD reads the line of text and will stop when it sees the ) character. So, if you typed CALL HCHAR( 1 2 42 4 ) WORD would capture 1 2 42 4 The output of WORD is two numbers: The address and length of the text that it found. This is fed into EVALUATE that simply evaluates the string as if it were a line of code entered at the keyboard. In this case, 1 2 42 4, or TEN TEN 42 TEN etc. are all valid code, so it executes it according to the rules of Forth: If we're compiling (i.e. the compiler was switched on with : (so we're building a word) then it will compile what it sees; If we're not compiling, it will just execute what it sees there and then, just like in BASIC when you enter something without a line number. So, we're using EVALUATE to evaluate the parameters for us between the HCHAR( word and the closing ) character. Note the cheeky use of the open parenthesis in HCHAR( which makes it look like some part of the the syntax of the word, but it isn't: It's just part of the name! And note also the closing parenthesis which again looks like syntax but is in fact nothing more than a marker for WORD to look for to isolate the parameters so that it can feed them into EVALUATE. The magic of Forth. The last bit of HCHAR( is very simple indeed. It just looks to see if we're in compile mode (the variable STATE will be 0 if we're not compiling, and >0 if we are compiling). If we ARE compiling, we compile a call to HCHAR (the original version of HCHAR built into the TurboForth EPROM). See? We're "injecting" code into the definition that is being compiled. However, if we're NOT compiling, we just execute HCHAR right there and then, which uses the parameters that EVALUATE evaluated for us. Thus we can do: CALL HCHAR( 1 2 42 99 ) (i.e. not in a definition, so it will execute immediately, like BASIC code with no line number) Or : LINE ( -- ) CALL HCHAR( 1 2 42 10 ) ; And both will work fine and do what they're supposed/expected to do. So, again, here's what happens when that LINE defintion above is compiled: The compilier sees that CALL is an immediate word, so it runs it. CALL actually does precisely nothing, it compiles nothing and runs nothing. It has 0 impact on run-time speed. It's purely "syntactic sugar" to sweeten things up for BASIC lovers. It's a total sham. You don't need to use it at all. The compiler sees that HCHAR( is immediate so it runs it. HCHAR( temporarily takes over, and reads the input up to the closing parenthesis and evaluates them. Since we're building a definition (LINE) the compiler is ON, so EVALUATE will compile them (by calling a new instance of the compiler and saying "HEY! Compile this! Thanks man!" (How's that for a mind f**k!?). HCHAR( then exits, it's done it's thing. Control now goes back to the compiler. The compiler only sees ; (semi-colon) because the parameters were consumed by WORD and EVALUATE so it completes the definition and we're done. If you were to disassemble the definition of LINE what you would see is this: 1 2 42 10 HCHAR In other words, HCHAR( re-arranged the code so that the parameters went first, then called a reference to the internal (in the EPROM) HCHAR which expects the parameters to be on the stack. The whole HCHAR( definition is nothing more than a trick which allows us to put the parameters *after* HCHAR( but internally it compiles HCHAR after the parameters. And that is the power of Forth. If you don't like: ROW COLUMN CHAR REPEATS HCHAR You can make your own word to give you: CALL HCHAR( ROW COLUMN CHAR REPEATS ) Or any other combination. And there endeth the lesson. This is without a doubt a bit of mind melter when you are new to Forth, so don't worry if you don't understand it all. I just wanted to give you an appreciation of the power and flexibility of Forth. It's not essential to understand this stuff right now. And now a quick demo using our new words. We haven't covered a lot of the code below yet. For now, just sit back and enjoy. : FWD-BOX ( -- ) 30 0 DO 12 0 DO CALL VCHAR( I I I 33 + J + 24 I 2* - ) CALL VCHAR( I 31 I - I 33 + J + 24 I 2* - ) CALL HCHAR( I I I 33 + J + 32 I 2* - ) CALL HCHAR( 23 I - I I 33 + J + 32 I 2* - ) LOOP LOOP ; : REV-BOX ( -- ) 0 29 DO 12 0 DO CALL VCHAR( I I I 33 + J + 24 I 2* - ) CALL VCHAR( I 31 I - I 33 + J + 24 I 2* - ) CALL HCHAR( I I I 33 + J + 32 I 2* - ) CALL HCHAR( 23 I - I I 33 + J + 32 I 2* - ) LOOP -1 +LOOP ; : BOXES ( -- ) \ top-level - run me 1 GMODE 5 0 DO FWD-BOX REV-BOX LOOP 0 GMODE ." Thanks for watching!" CR ; Note the additional spaces in the paremeters so that it's easier to identify each paremeter. References: HCHAR - http://turboforth.net/lang_ref/view_word.asp?ID=220 VCHAR - http://turboforth.net/lang_ref/view_word.asp?ID=232 IMMEDIATE - http://turboforth.net/lang_ref/view_word.asp?ID=163 ASCII - http://turboforth.net/lang_ref/view_word.asp?ID=210 STATE - http://turboforth.net/lang_ref/view_word.asp?ID=189 COMPILE - http://turboforth.net/lang_ref/view_word.asp?ID=156 IF - http://turboforth.net/lang_ref/view_word.asp?ID=81 THEN - http://turboforth.net/lang_ref/view_word.asp?ID=88 ELSE - http://turboforth.net/lang_ref/view_word.asp?id=76 CONSTANT - http://turboforth.net/lang_ref/view_word.asp?ID=157 CR - http://turboforth.net/lang_ref/view_word.asp?ID=129 ." - http://turboforth.net/lang_ref/view_word.asp?ID=206 DO - http://turboforth.net/lang_ref/view_word.asp?ID=75 LOOP - http://turboforth.net/lang_ref/view_word.asp?id=84 +LOOP - http://turboforth.net/lang_ref/view_word.asp?id=69 I - http://turboforth.net/lang_ref/view_word.asp?ID=80 J - http://turboforth.net/lang_ref/view_word.asp?ID=82