neglectoru Posted November 21, 2021 Share Posted November 21, 2021 I've written several forth interpreters, I've read Brodie's books, and I have a lot of experience in the traditional Algol world of languages (Java, C, Pascal, etc.) I have even used my forth as a scripting language in games I've written (small interaction scripts for an RPG, for example.) I understand forth, but I honestly don't feel like I "get" it. I wrote some games for the Jupiter Ace, but the result was a mess I couldn't follow when I came back to it weeks later. Every time I try to write something big in Forth, I feel like I hit a wall. I think part of that is that I've gained so much from reading code, which is in abundance in the above languages, and really isn't for Forth. I've studied far more forth interpreters than programs written in Forth! Maybe Forth is better for radio telescopes or device drivers or firmware, which is not really the kinds of programs I'm drawn to. Are there good, idiomatic, substantially sized Forth programs that the experts recommend? I'd really like to see some non-trivial games, but really any highly interactive program would be interesting. I feel like if I read some good Forth programs, mine wouldn't be so awful. (I'm definitely not trying to troll here. I love programming, and I'd love to try to take advantage of what Forth has to offer.) 2 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted November 22, 2021 Share Posted November 22, 2021 I think, if I May add my 2 cents here, that documentation is crucial. The small space provided within your code following The slash mark is definitely a joke on forth users. I mean, you can say general notes as in, \ magic here. But I always keep seperate documentation that really tells what my values are for and what each piece of program is doing. I would think it doesn't become large after that, just a routine that takes care of a situation. 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted November 22, 2021 Share Posted November 22, 2021 3 hours ago, neglectoru said: I've written several forth interpreters, I've read Brodie's books, and I have a lot of experience in the traditional Algol world of languages (Java, C, Pascal, etc.) I have even used my forth as a scripting language in games I've written (small interaction scripts for an RPG, for example.) I understand forth, but I honestly don't feel like I "get" it. I wrote some games for the Jupiter Ace, but the result was a mess I couldn't follow when I came back to it weeks later. Every time I try to write something big in Forth, I feel like I hit a wall. I think part of that is that I've gained so much from reading code, which is in abundance in the above languages, and really isn't for Forth. I've studied far more forth interpreters than programs written in Forth! Maybe Forth is better for radio telescopes or device drivers or firmware, which is not really the kinds of programs I'm drawn to. Are there good, idiomatic, substantially sized Forth programs that the experts recommend? I'd really like to see some non-trivial games, but really any highly interactive program would be interesting. I feel like if I read some good Forth programs, mine wouldn't be so awful. (I'm definitely not trying to troll here. I love programming, and I'd love to try to take advantage of what Forth has to offer.) You clearly know how to code so the words below are just me talking. Maybe it's useful. If not flush it. (not to disk) As you know Forth is not a popular language these days so the amount of code you find online is pretty small. Forth is mostly used for niche applications where size is critical or very low level control is required but Assembler is not productive enough or not well enough known for the specific processor. The niche getting smaller in a world where tiny uPs have 256K of RAM. One of the things that is hardest to learn when trying to write a substantial program in Forth is the understanding that you really don't want to program in "Forth". (that's my opinion) To use it well you actually create a new language that helps you write the program. This makes the front-end of the project more challenging but can make the back end and modifications simpler. I have been looking at a game that was published in 1982 in Byte. It was written in Forth, but clearly by someone who had a solid background in BASIC and then wrote the game like it was BASIC only using Forth statements. It is arguably the worst example of how to write Forth code I have ever seen. I am writing some of my thoughts on that here: CAMEL99-V2/On Forth Coding Style.md at master · bfox9900/CAMEL99-V2 (github.com) I think one the best examples I have of Forth "style" in the Vibe editor by Sam Falvo. It's not a game but it shows the point. I have ported it to TI-99 for my own education with permission from Sam. I removed the extra commands that I added in this listing so not to obscure Sam's style. Interesting things to notice. There are four variables. The definitions are very short. The word definitions become part of how you describe to solution to make the editor. There is no loop structure used to list the text on the screen. The loop is un-wound because calling overhead is less than the loop overhead. There is no case statement for the command control. What??? The Forth interpreter's dictionary search mechanism is the case statement. Each command is looked up by dynamically creating a short string from the key stroke pressed and passing the string to FIND. One thing I don't agree with in this example is the stack diagram comments have been omitted. Using them allows new people to understand your Forth code much faster and with a memory like mine they help me remember my own intentions. If you struggle to understand your code a few weeks later then you might be writing long definitions that make understanding the stack nearly impossible a few lines into it. The secret to making Forth understandable from what I read from Chuck Moore and others is factor factor factor. Even if you do that however you still have to work extra hard at creating the right names for things so that their purpose is implied by the name. That's more art than science in my experience. Spoiler \ VIBE Release 2.2 \ Copyright (c) 2001-2003 Samuel A. Falvo II \ All Rights Reserved \ * Use with written permission for Camel99 Forth * \ Highly portable block editor -- \ USAGE: VI <filepath> opens BLOCK FILE, \ VI (no parameter) goto last used block \ VIBE ( n -- ) Edits block 'n'. Sets SCR variable to 'n'. \ \ 2.1 -- Fixed stack overflow bugs; forgot to DROP in the non-default \ key handlers. \ \ 2.2 Ported to CAMEL99 Forth B. Fox 2019 \ Removed some character constants to save space. \ Changed TYPE for VTYPE. \ Removed shadow block function \ Added some block navigation commands \ 2.3 Fixed keyboard bugs for TI-99/4A \ VI command takes a filename parameter like real VI \ simplfied wipe screen logic and saved bytes \ Add $ command: goto end of line \ Add PC delete KEY for Classic99 NEEDS DUMP FROM DSK1.TOOLS NEEDS 80COLS FROM DSK1.80COL NEEDS RKEY FROM DSK1.RKEY NEEDS BLOCK FROM DSK1.BLOCKS NEEDS -TRAILING FROM DSK1.TRAILING NEEDS MARKER FROM DSK1.MARKER MARKER /VIBE HERE ( Editor Constants ) CHAR i CONSTANT 'i \ Insert mode CHAR c CONSTANT 'c \ Command mode \ camel99 values DECIMAL 64 CONSTANT WIDTH 80 CONSTANT MAXBLKS ( Editor State ) VARIABLE SCR \ Current block VARIABLE X \ Cursor X position 0..63 VARIABLE Y \ Cursor Y position 0..15 VARIABLE MODE \ current mode: INSERT or command ( 'i OR 'c \ CMDNAME the command string, is built, found and executed CREATE CMDNAME 5 C, CHAR $ C, CHAR $ C, 0 C, 0 C, 0 C, ( Editor Display ) DECIMAL : BLANKS BL FILL ; \ BF add : MODE. 63 0 AT-XY MODE @ EMIT ; : VTYPE ( addr len -- ) TUCK VPOS SWAP VWRITE VCOL +! ; : SCR. 0 0 AT-XY S" Block: " VTYPE SCR @ . ( S" " VTYPE ) ; : HEADER SCR. MODE. ; : 8-S S" --------" VTYPE ; : WIDTH-S 8-S 8-S 8-S 8-S 8-S 8-S 8-S 8-S ; : BORDER SPACE WIDTH-S CR ; : ROW ( addr -- addr') DUP 63 VTYPE 64 + ; \ FAST \ : ROW ( addr -- addr') DUP 63 TYPE 63 + ; \ SLOW : LINE ." |" ROW CR ; : 4LINES LINE LINE LINE LINE ; : 16LINES SCR @ BLOCK 4LINES 4LINES 4LINES 4LINES DROP ; : CARD 0 1 AT-XY BORDER 16LINES BORDER ; : CURSOR X @ 1+ Y @ 2+ AT-XY ; : SCREEN HEADER CARD CURSOR ; ( Editor State Control ) : INSERT 'i MODE ! ; : REPLACE [CHAR] r MODE ! ; : CMD 'c MODE ! ; : BOUNDED ( addr n -- ) 0 MAX MAXBLKS MIN SWAP ! ; : PREVBLOCK SCR DUP @ 1- BOUNDED ; : NEXTBLOCK SCR DUP @ 1+ BOUNDED ; \ : TOGGLESHADOW 1 SCR @ XOR SCR ! ; ( Editor Cursor Control ) : FLUSHLEFT 0 X ! ; : BOUNDX X @ 0 MAX 63 MIN X ! ; : BOUNDY Y @ 0 MAX 15 MIN Y ! ; : BOUNDXY BOUNDX BOUNDY ; : LEFT X 1-! BOUNDXY ; : RIGHT X 1+! BOUNDXY ; : UP Y 1-! BOUNDXY ; : DOWN Y 1+! BOUNDXY ; \ : beep 7 EMIT ; : NEXTLINE Y @ 15 < IF FLUSHLEFT DOWN THEN ; : NEXT X @ 63 = IF NEXTLINE EXIT THEN RIGHT ; ( Editor Insert/Replace Text ) : 64* 6 LSHIFT ; \ x64 : WHERE SCR @ BLOCK SWAP 64* + SWAP + ; : WH X @ Y @ WHERE ; : SOL 0 Y @ WHERE ; : EOL 63 Y @ WHERE ; : PLACE WH C! UPDATE NEXT ; : -EOL? X @ 63 < ; : OPENR WH DUP 1+ 63 X @ - CMOVE> ; : OPENRIGHT -EOL? IF OPENR THEN ; : INSERTING? MODE @ 'i = ; : CHR INSERTING? IF OPENRIGHT THEN PLACE ; : EOTEXT SOL 63 -TRAILING NIP X ! ; : NEXTWORD WH EOL OVER - DUP -ROT ( len adr len) BL SKIP \ skip spaces BL SCAN \ find next space NIP - 1+ X ! BOUNDX WH ; ( Editor Keyboard Handler CMDWORD encoding) \ CMD name key: $ $ _ _ _ \ | | | \ 'c'=command mode --+ | | \ 'i"=ins/repl mode | | \ | | \ Key code (hex#) -----+-+ \ \ Called with ( k -- ) where k is the ASCII key code. ( Editor COMMANDS: Quit, cursor, block, et. al. ) ( Modified for Ti-99 keyboard ) : $$c51 DROP 0 19 AT-XY R> R> DROP >R ; \ : -- quit main loop : $$c30 DROP FLUSHLEFT ; \ 0 goto start of line : $$c24 DROP EOTEXT ; \ $ goto end of line : $$c69 DROP INSERT ; \ i : $$c49 DROP FLUSHLEFT INSERT ; \ I : $$c52 DROP REPLACE ; \ R : $$i0F DROP CMD ; \ (escape) GOTO command mode : $$c68 DROP LEFT ; \ h : $$c6A DROP DOWN ; \ j : $$c6B DROP UP ; \ k : $$c6C DROP RIGHT ; \ l : $$c5B DROP PREVBLOCK ; \ [ \ : $$c5C DROP TOGGLESHADOW ; \ \ : $$c5D DROP NEXTBLOCK ; \ ] : $$c77 DROP NEXTWORD ; \ w ( Editor Backspace/Delete ) : PADDING BL EOL C! UPDATE ; : DEL WH DUP 1+ SWAP 63 X @ - CMOVE ; : DELETE -EOL? IF DEL THEN PADDING ; : BS LEFT DELETE ; : BACKSPACE X @ 0 > IF BS THEN ; ( Editor Carriage Return ) : NEXTLN EOL 1+ ; : #CHRS SCR @ BLOCK 1024 + NEXTLN - WIDTH - ; : COPYDOWN Y @ 14 < IF NEXTLN DUP WIDTH + #CHRS CMOVE> THEN ; : BLANKDOWN NEXTLN WIDTH BLANKS UPDATE ; : SPLITDOWN WH NEXTLN 2DUP SWAP - CMOVE ; : BLANKREST WH NEXTLN OVER - BLANKS ; : OPENDOWN COPYDOWN BLANKDOWN ; : SPLITLINE OPENDOWN SPLITDOWN BLANKREST ; : RETRN INSERTING? IF SPLITLINE THEN FLUSHLEFT NEXTLINE ; : RETURN Y @ 15 < IF RETRN THEN ; ( Editor Wipe Block ) \ simplified by BFox HEX : >UPPER ( c -- c') 5F AND ; DECIMAL : PROMPT 0 19 AT-XY ; : MSG PROMPT ." Are you sure? (Y/N) " ; : CLRMSG PROMPT WIDTH SPACES ; : NO? MSG KEY >UPPER CLRMSG [CHAR] Y <> ; : ?CONFIRM NO? IF R> DROP THEN ; : WIPE ?CONFIRM SCR @ BLOCK 1024 BLANKS UPDATE 0 X ! 0 Y ! ; ( Editor Commands: backspace, delete, et. al. ) : $$i04 DROP DELETE ; \ ^D : $$i03 DROP DELETE ; \ PC delete key : $$i08 DROP BACKSPACE ; \ Backspace \ : $$i7F DROP BACKSPACE ; \ DEL -- for Unix : $$i0D DROP RETURN ; \ Enter : $$c5A DROP WIPE ; \ Z : $$c6F DROP OPENDOWN DOWN $$c49 ; \ o : $$c4F DROP OPENDOWN ; \ O : $$i15 DROP X OFF Y OFF ; \ "HOME" key INSERT mode : $$c15 $$i15 ; HEX 0F CONSTANT $0F F0 CONSTANT $F0 : KEYBOARD RKEY 7F AND ; \ for TI-99 we need to mask upper bit DECIMAL : CMD? MODE @ 'c = ; : INS? MODE @ 'i = MODE @ [CHAR] r = OR ; : MODE! INS? 'i AND CMD? 'c AND OR CMDNAME 3 + C! ; : >HEX DUP 9 > IF 7 + THEN [CHAR] 0 + ; : H! DUP $F0 AND 4 RSHIFT >HEX CMDNAME 4 + C! ; : L! $0F AND >HEX CMDNAME 5 + C! ; : NAME! MODE! H! L! ; : NOMAPPING DROP ['] HONK CMD? AND ['] CHR INS? AND OR ; \ : .CMDNAME 68 0 AT-XY CMDNAME COUNT TYPE ; \ debugging : HANDLERWORD NAME! CMDNAME ( .CMDNAME) FIND 0= IF NOMAPPING THEN ; : HANDLER DUP HANDLERWORD EXECUTE ; : EDITOR 'c MODE ! BEGIN KEYBOARD HANDLER SCREEN ?BREAK AGAIN ; : VIBE ( n -- ) DECIMAL SCR ! PAGE SCREEN EDITOR ; \ \\\\\\\\\\\\\\\\\\\\\\\\\\ VIBE ENDS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 4 Quote Link to comment Share on other sites More sharing options...
speccery Posted November 22, 2021 Share Posted November 22, 2021 Thanks @neglectoru for sharing your thoughts. I would also be interested in seeing a substantial Forth program. Similarly to what I understood from you, I have written a couple of Forth implementations. One for MC68000 and another I ported/wrote for MC6811. I have used Forth to debug hardware and test low level things, but I haven't used it for anything I would call substantial. For me that would mean thousands of lines of code - I know that code size is not a good metric, but I routinely write C or C++ programs of thousands of lines (or tens of thousands), but have never done something similar in Forth. Maybe its just my unfamiliarity with Forth, combined with the lack of proper debugging tools (or knowledge of such tools). If I write something in C/C++, I can look at the code years later and understand what the code is doing quickly. If I look at some Forth code, it always takes some head scratching to figure out what's going on. Probably some of that has to do with library code, with C/C++ and I know what the common libraries do, but I don't have similar familiarity with Forth... The example @TheBF provided above is interesting (and I have not read it yet, looks very compact) but would not qualify as a big program. 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted November 22, 2021 Share Posted November 22, 2021 Took some looking but here is one. https://github.com/nnCron/nnCron 1 Quote Link to comment Share on other sites More sharing options...
danwinslow Posted November 22, 2021 Share Posted November 22, 2021 I wrote a few simple games in Valforth on the Atari. One of my thoughts about Forth is that it's not really a 'language to write a program in'. It's more of an 'engine to define a language to write a program in'. In essence, it's kind of like using assembler - you build all of your own language constructs. If you do it in an haphazard, informal way, you'll probably hit a wall at a certain level of complexity. That's why you need to define your words in a consistent, highly factored, loosely coupled way...just like language designers do when designing a language. 2 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted November 22, 2021 Share Posted November 22, 2021 Here's the JetPac source written in TurboForth. It is quite involved, so feel free to ask questions. Jetpac.txt 7 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted November 22, 2021 Share Posted November 22, 2021 (edited) Something else is worth noting regarding Forth tool chains. The Forth's that people are exposed to or build themselves are many times using concepts from the 1970s and 80s. All modern Forth systems are not using threaded code. It doesn't make much sense with 32bit or larger machines. So if you want to see what commercial Forth tools are like you can download the two big systems for non-commercial use for free. VFX generates faster code and some people like it better. Their compilers were used for some European space missions and for a 1 million line project called Candy. Compile time is ~one minute I believe for 1 M LOC. They also have compilers for a Forth CPU (RTX2000) used in space missions like Galileo and the one that visited the comet. (Rossetta?) https://www.mpeforth.com/software/pc-systems/vfx-forth-for-windows/ SwiftForth is from Forth Inc. the first Forth company, headquartered in USA. https://www.forth.com/download/ Forth Inc. has a long list of clients. https://www.forth.com/resources/who-uses-forth/ SP Forth is also a native code generating compiler from some very clever folks from Russia. They developed the nnCron product and something called EServ. The site has a lot Russian which I don't read unfortunately, but Chrome translates reasonably well. http://spf.sourceforge.net/ Here is a listing of the rather significant library set for SP.Forth. http://spf.sourceforge.net/docs/devel.en.html All that to say these are not like the Forth's we see used on our legacy systems. These may affect the quality of a project. However using a legacy system didn't slow down @Vorticon in anyway with JetPack. Nice work there. Edited November 22, 2021 by TheBF Fixed links to Forth Inc. 2 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted November 22, 2021 Share Posted November 22, 2021 I'd have to also say, using Forth repeatedly as a go to language will also make reviewing code a bit easier because of it's structure compared to other languages. But making those important notes and even video reviews of your code as your working and saving for another day helps refresh your thinking. Sometimes we look at our old code and say, what was I thinking when I did that! Quote Link to comment Share on other sites More sharing options...
speccery Posted November 23, 2021 Share Posted November 23, 2021 @TheBF thanks for compiling that list, those are indeed impressive projects! The VFX Forth debugger etc. look interesting, and probably make it a very productive environment for Forth development. 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted November 23, 2021 Share Posted November 23, 2021 I had not looked at MPE's web site for a long time. Stephen Pelc, the founder told me 10 years ago that a company with a "fruit" in the logo used their new VFX compilers. I guess it's more public now. From: https://vfxforth.com/ (I Bolded the fruit company name) CUSTOMERS On some consultancy jobs clients have asked us to sign non-disclosure agreements covering not only the work but the client name as well. They regard Forth as a competitive advantage for their work. The following is thus just a partial list from our customer base. Apple, Astell Scientific, AWE, BAE, British Telecom, British Gas, Brown Root Vickers, CDS Advanced Technology bv, CEGB, Cementation, CMON Systems, Construction Computer Software, Department of Transport, Disarmco, Europay International, Farnell, GEC Plessey Avionics, Intersil, ISRO, ITT, JWK International, Lucas CAV, Marconi Space and Defence, Metropolitan Police, NASA, Nuclear Electric, Philips, Rolls Royce, Rushton Diesels, Rutherford Appleton Labs, Saab, Safer Systems, Shell UK Oil, Sun Microsystems, Thorn EMI, Trafalgar House, Yorkshire TV, and many universities and research labs. On one space shuttle flight, three out of four experiments were programmed in Forth, each team having chosen Forth individually. Forth is approved by NASA for high reliability applications. 2 1 Quote Link to comment Share on other sites More sharing options...
Elia Spallanzani fdt Posted November 23, 2021 Share Posted November 23, 2021 5 Quote Link to comment Share on other sites More sharing options...
GDMike Posted November 23, 2021 Share Posted November 23, 2021 (edited) Haha .I don't know who's playing this but struggling already on screen 3. Lol Good writing here within this program. Nice job Edited November 23, 2021 by GDMike 1 Quote Link to comment Share on other sites More sharing options...
+FarmerPotato Posted November 23, 2021 Share Posted November 23, 2021 Sam Falvo's editor reads amazing. From my experience: My career has been mostly C, C++, Objective-C. I've learned (about myself) that I get good-looking code after rewriting it two, three times as I get better ideas. The biggest Forth program I've done was a FAT32 file system reader for SD card/SPI (not even the writer..) I thought I was on the right track, defining focused words, but even after it worked, I had trouble seeing how next to build on it. I used bottom-up programming: read/write a byte, send a command sequence, fill a buffer, derive some numbers, follow a linked list, manage buffers... At the same time I had a clear idea of what the high-level must be: find a file, read a file. Those are pretty concrete goals without much gap in between. Then I see other folks' FAT32 code in Forth, it's about a screenful, and it makes mine look crazy. The other Forth I've written has been short unit tests for my V9958 card. With Forth I know that as soon as I put a control structure FOR..NEXT or DO..WHILE..LOOP or even IF..ELSE..THEN, I had better get everything else out of that word! Like : GO BOUNDS DO I ACT LOOP ; has all the stuff moved into "BOUNDS" and "ACT". If I don't do that, the code will be a mystery later. Imaginary monster word: : MAIN ( initialize a bunch of variables ) ( figure out what the task is ) ( big case statement ) ( prepare for the task ) ( some conditional logic ) ( some tricky loops ) ( closing statements of tricky loops ) ( more closing statements ) ( even more nesting closing statements ) ( results are written ) ; All it takes is one unbalanced stack event, and it's a hopeless mess to work on. With giant messy C programs, at least you create higher-level bugs (and the solution is to write tests, then break it up.) I learn more about factoring, continuously. I find myself writing 2-3 times more code in tests, than in the working program. Forth is said to take a "bottom up" approach, but early on, that idea left me wondering how to ever see the way to the top? I felt a similar stupefaction when Chuck Moore said "today's computers are so powerful, you don't need an operating system" [besides Forth.] What, build the whole thing from the bottom up? It's just too many levels for one person (me) to get right. So with bigger programs, I usually have no picture of what is going to be in between top and bottom. And I really struggle with "AI", in the sense of a game-playing algorithm. Good thing that I get paid to code "read data, compute, write data, maybe build up some state, repeat", which is atop the real-world complexity (standards docs, hundreds of data types, client-server, transaction state, persistence, weird file formats...) So I know that when I sit down to write a C program, I'm going to be chopping up my code until the good result is 100s of lines of code rewritten 2-3 times. I never got into that kind of groove with Forth. Maybe frustrated by the editor. I sure wish I had had big, model Forth programs long ago. 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted November 23, 2021 Share Posted November 23, 2021 Lot's of points in that post Erik. All it takes is one unbalanced stack event, and it's a hopeless mess to work on. I'm thinking you didn't test the words in the interpreter as you went along. The console is the debugger. IMHO it's the compensation for the whacky stack language. If you have a big definition that is messing up the data stack, cut out each line, name them and test them one at a time. Then put them back into the definition. Forth is said to take a "bottom up" approach, Forth let's you build bottom up. Design top down is still where you begin I think. The actual process is circular because with Forth you can write 5 lines and test your assumptions even about hardware details, rather that waiting until the whole thing is finished and you realize that interrupts actually take 3 times longer than the design spec. ? Maybe your design part was weaker than ideal? Maybe frustrated by the editor. Yes that can happen with a block editor. Not for everybody. That's why the commercial systems all let you set a path for where your editor is. Here is some code that indicates where Forth goes in the hands of these full-time Forth guys. Below is code from VFX Forth. Doesn't look like my Forth code. These guys programmed Forth to become what they needed. That's the ticket IMHO but also why when you come from a conventional "this is what you get" compiler it takes some getting used to. Is it easier? Probably not in the beginning, but it gets easier the more you make it what you need. #define IDW_FORTHWINDOW $3801 IDW_FORTHWINDOW WINDOW 0, 0, 100, 100 BEGIN Caption "Forth Console" Style WS_CHILD | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX ExStyle WS_EX_MDICHILD ClassStyle CS_HREDRAW | CS_VREDRAW Icon IDI_APPLICATION Cursor IDC_ARROW Brush WHITE_BRUSH Menu NULL WndProc ForthWindowProc END DialogBox: TextDlg 50 10 110 50 Position&Size Modal Caption "Enter Name" WS_VISIBLE Style WS_CAPTION WS_POPUP or Style+ WS_BORDER Style+ 8 100 0 Font "MS Sans Serif" 5 5 100 12 IDC_EditBox z" Hello" EditBox 10 25 40 15 IDC_OK z" &OK" PushButton 60 25 40 15 IDC_CANCEL z" &Cancel" PushButton WM_INITDIALOG does TB-DoInit WM_COMMAND does TB-DoCommand end-dialog 2 Quote Link to comment Share on other sites More sharing options...
neglectoru Posted November 24, 2021 Author Share Posted November 24, 2021 (edited) Thanks for the detailed examples! I will definitely study them in more detail. My big question is often about design, which just feels so foreign to me. Here is a concrete example of a toy problem, and where I run into issues. Not game related, but easy to explain quickly. The concrete problem: You have a list of calendar events. Given a date (year, month, day), print all the events on that day. A calendar event is a string (event name), date/time (year, month, day, hour, minute), a duration (minutes), a "repeating flag", and an end date. For simplicity, if repeating flag = 0, no repeat, flag = 1, every day, flag = 2, every week. the end date (year, month, day) is ignored if e.g., a simple 30 minute non-repeating event: ("E1", 2021, 11, 23, 16, 30, 15, 0, 0, 0, 0) = An event that starts on 2021-Nov-23 at 16:30 and ends at 16:45 ("E2", 2021, 11, 23, 16, 30, 15, 1, 2021, 11, 27) = An event at 2021-Nov-23 16:30, 2021-Nov-24 16:30, ... 2021-Nov-27 16:30 ("E3", 2021, 11, 23, 16, 30, 15, 2, 2021, 12, 15) = Same idea, 2021-Nov-23, 16:30, 2021-Nov-30 16:30, 2021-7-Dec 16:30, 2021-Dec-14 16:30 The algorithm is straightforward to describe Given a target date: For each event: If event flag is 0 and target date is event date, print it If event flag is 1 and target date >= event date and target date <= end date, print it If event flag is 2 and target date >= event date and target date <= end date and day-of-week(target-date) equals day-of-week(event date), print it I know exactly how I'd implement this in C, or Javascript, or Python, but Forth? Let's start with representation. For both C and Forth, we can have a count of events, and each event can be a fixed size number of words (10, plus however you represent the event string) My first problem is that every Forth system handles strings differently, so let's drop that problem for now. Problem 2, how do you get fields? In C, this is straightforward: int[] allEvents = [........] // so year(event[2]) is allEvents[event * 10 + 0), month is allEvents[event * 10 + 1], etc int getDay(int event) { return allEvents[event * 10 + 2]; } in Forth, I suppose you'd do something similar : GET-DAY (event-id -- day) 10 * 2 + allEvents + @ ; or something like that. I find that hard to read, but maybe you get used to it. Problem 3, you need to compare dates. As this point, I'm somewhat lost. int equalDates(int y1, int m1, int d1, int y2 int m2, int d2) { return (y1 == y2) && (m1 == m2) && (d1 == d2); } in Forth, that becomes : EQUAL-DATES (y1 m1 d1 y2 m2 d2 -- f) (I have no idea) ; Plus, consider the fact that we need to compare dates more than once, so the stack will always have the target date on the bottom, so you need to do something like 3DUP before you consume a date, because you need to preserve the date. As for the final algorithm, my resulting C code would ultimately look like the description I gave above) void printAgenda(int targetYear, int targetMonth, int targetDay) { for (int i=0; i < numDates; i++) { if ((getFlag(i) == 0) && equalDates(getYear(i), getMonth(i), getDay(i), targetYear, targetMonth, targetDay)) { // print the event ... ... } Is that pretty? It's not great. I could do better, but I can sit down and understand what it does later. In Forth, I'm not at all sure how I'd write this in a way I could read it. I definitely, for example, need a Forth FOR loop because I need to access "I" all the time ... i GET-YEAR I GET-MONTH I GET-DAY (now stack has year, month, and day) .... but what now? How do I get targetYear, month, and day to the top of the stack so I can compare them? Again, maybe it's my Algol-language training, but I'm pretty sure I could write this in a few more minutes in C, and Forth would take me hours. Edited November 24, 2021 by neglectoru better formatting 1 1 Quote Link to comment Share on other sites More sharing options...
+FarmerPotato Posted November 24, 2021 Share Posted November 24, 2021 7 hours ago, TheBF said: All it takes is one unbalanced stack event, and it's a hopeless mess to work on. I'm thinking you didn't test the words in the interpreter as you went along. The console is the debugger. IMHO it's the compensation for the whacky stack language. If you have a big definition that is messing up the data stack, cut out each line, name them and test them one at a time. Then put them back into the definition. It's a hopeless mess so long as it is one monster word. Your solution is entirely correct! Then I got into situations where a word had different outcomes and I wanted to leave "0" or "d d 1", which could go wrong. 7 hours ago, TheBF said: Forth is said to take a "bottom up" approach, Forth let's you build bottom up. Design top down is still where you begin I think. The actual process is circular because with Forth you can write 5 lines and test your assumptions even about hardware details, rather that waiting until the whole thing is finished and you realize that interrupts actually take 3 times longer than the design spec. ? Maybe your design part was weaker than ideal? I think my difficulty was thinking in JMP and GOTO. I had not yet encountered so many C programs that are a big while() at the top level, but I get that now. Drawing the control flow on paper would help me see where to divide it into words. Now at work I have mastered the discipline of writing very short C and Objective-C functions! I "optimize" less. Instead of inlining some one-line (but complex) expression as I go, I write little methods, with verbose names. For instance just to test if an item exists a couple levels deep into a data structure. Then I have if(thingExists(dictionary, thing)) and not if (dictionary[thingAssets] && dictionary[thingAssets][thing]) { do something with dictionary[thingAssets][thing] } The compiler will optimize all that for me. I have finally, finally absorbed the lesson from freshman comp sci: "premature optimization is root of all evil. (Knuth)" Small functions from here on out. 7 hours ago, TheBF said: Maybe frustrated by the editor. Yes that can happen with a block editor. Not for everybody. That's why the commercial systems all let you set a path for where your editor is. I'm trying to make my favorite editor launch gforth. I think I need a global dictionary entry "#! /usr/local/bin/gforth". I was typing my pre-written FORTI code into fbForth, it does have an improved block editor. Looking forward to REQUIRE. But where I used the most effort was launching meCrisp on BlackIce II, then pasting in all the words so far. (it doesn't have files... maybe I I finish that FAT32 system...) Then developing a little and resetting. I can re-compile meCrisp with my dictionary built-in, but I reserved that for when I was really sure I had words finalized. 4 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted November 24, 2021 Share Posted November 24, 2021 (edited) It could take me hours as well because I would have to work up the higher level words to create the calendar etc. and I am not that smart. Forth is actually a macro assembler for the Forth VM so low level. Chuck Moore is a fanatic about simplicity. He doesn't believe in general solutions because "I have never seen the general problem" So you do have to build more foundation yourself than C. The commercial systems come with a pile of library code. Forth Inc has a SQL library. Most old hands have their own personal library that they customize as needed. --------------- Let me see what happens if "spitball" your questions: (this is dangerous) A calendar event is a string (event name), date/time (year, month, day, hour, minute), a duration (minutes), a "repeating flag", and an end date. For simplicity, if repeating flag = 0, no repeat, flag = 1, every day, flag = 2, every week. the end date (year, month, day) is ignored if e.g., a simple 30 minute non-repeating event: I would keep the records in a BLOCK file which makes the data look like virtual memory. For what you show here in small system like TI-99 I would limit the record size to 32 bytes so 32 records per 1K file block. With that record size could fit 366 days for a leap year, with 15 events per day max on a 180K DSSD floppy disk. On a modern system that could be much bigger. \ returns the address of the record in virtual memory : ]RECORD ( rec# -- addr) B/REC * 1024 /MOD BLOCK + ; : [REC ACTIVE @ ]RECORD ; My first problem is that every Forth system handles strings differently, so let's drop that problem for now. This can be true but the ANS has made some progress in making the primitives more standard. All the commercial systems provide libraries. So too, all the TI-99 Forths. Problem 2, how do you get fields? In C, this is straightforward: ANS Forth has structs I my library I defined CELL: and CHAR: using the primitive word +FIELD. Even here Forth gives you primitives. You make the rest. \ We can access the fields using ANS Forth structures 0 ( accumulator ) 9 CHARS: EVENT] CELL: YEAR] CHAR: MONTH] CHAR: DAY] CHAR: HOUR] CHAR: MINS] CELL: DURATION] ( mins) CHAR: FLAG] CELL: YEAR2] CHAR: MONTH2] CHAR: DAY2] CONSTANT REC-SIZE So with the struct words and [REC we can access a disk records like this: 5 ACTIVE ! ( sets the active record) [REC YEAR] @ . ( fetch and print integer) [REC FLAG] C@ . ( fetch byte and print it) Since you have defined your event like that I would be temped to make the code look like that. These lines put data to disk records. 0 REC# '( " E1" 2021,11,23,16,30,15,0,0,0,0 )EVENT 1 REC# '( " E2" 2021, 11, 23, 16, 30, 15, 1, 2021, 11, 27 )EVENT 2 REC# '( " E3" 2021, 11, 23, 16, 30, 15, 2, 2021, 12, 15 )EVENT 3 REC# '( " E4" 2021,11,23,16,30,15,0,0,0,0 )EVENT 4 REC# '( " E5" 2021, 11, 23, 16, 30, 15, 1, 2021, 11, 27 )EVENT 5 REC# '( " E6" 2021, 11, 23, 16, 30, 15, 2, 2021, 12, 15 )EVENT 6 REC# '( " E7" 2021,11,23,16,30,15,0,0,0,0 )EVENT 7 REC# '( " E8" 2021, 11, 23, 16, 30, 15, 1, 2021, 11, 27 )EVENT 8 REC# '( " E9" 2021, 11, 23, 16, 30, 15, 2, 2021, 12, 15 )EVENT I know exactly how I'd implement this in C, or Javascript, or Python, but Forth? To be fair that's a bit like saying "I know exactly how to write that in French, or German, or Dutch but Russian?" Plus, consider the fact that we need to compare dates more than once, so the stack will always have the target date on the bottom, so you need to do something like 3DUP before you consume a date, because you need to preserve the date. If you are doing that then something is wrong. You should never be accessing deeper that 3 items 4 absolute MAX into the data stack. Factor that sh*t out. If you really have a complex problem, then ANS Forth now has locals on a stack frame like C, but there is performance hit versus doing it without stack frames. I have not written it yet but I would do: TIME@ >SECONDS returning a long integer value (32bit on TI-99) With that method you will just read the field from each record and reduce each one to seconds then do 32bit compare (D=) and that's it. There would 2 items per double on the stack but because you use 32bit operators you don't need to know that. In Forth, I'm not at all sure how I'd write this in a way I could read it. I definitely, for example, need a Forth FOR loop because I need to access "I" all the time Forth has the DO LOOP with an index called I , (J , K) for nested loops. So all that to say here is a very simple database that writes to disk for your data. I am an amateur here. I left engineering work over 25 years ago and worked on the business side of things so I am re-learning this stuff for my own amusement. And... trying to remember all the crap in the libraries that I wrote over the last 2 years. However you can see that you don't actually write Forth like C or any other language for that matter. You can... but you will hate it. Maybe that's why so many people do? :))) Spoiler \ Example calendar data base INCLUDE DSK1.TOOLS INCLUDE DSK1.BLOCKS INCLUDE DSK1.STRUC12 INCLUDE DSK1.UDOTR INCLUDE DSK1.CASE DECIMAL \ 30 S" DSK5.CALENDAR" MAKE-BLOCKS S" DSK5.CALENDAR" OPEN-BLOCKS 32 CONSTANT B/REC VARIABLE ACTIVE : REC# ( n -- ) ACTIVE ! ; \ returns the address of the record in virtual memory : RECORD ( rec# -- addr) B/REC * 1024 /MOD BLOCK + ; : [REC ACTIVE @ RECORD ; \ use with words that end with ']' \ We can access the fields using ANS Forth structures 0 ( accumulator ) 9 CHARS: EVENT] CELL: YEAR] CHAR: MONTH] CHAR: DAY] CHAR: HOUR] CHAR: MINS] CELL: DURATION] ( mins) CHAR: FLAG] CELL: YEAR2] CHAR: MONTH2] CHAR: DAY2] CONSTANT REC-SIZE : " ( -- addr len) [CHAR] " PARSE-WORD ; : PARSE-INT ( -- n ) [CHAR] , PARSE-WORD EVALUATE ; HEX : PARSE-BYTE ( -- c ) [CHAR] , PARSE-WORD EVALUATE DUP FF00 AND ABORT" Bad byte value" ; DECIMAL : ?EVENT ( addr len -- addr len) DEPTH 2 < ABORT" Quoted string expected" DUP 9 > ABORT" String too long" ; : '( [CHAR] " PARSE-WORD [REC EVENT] PLACE PARSE-INT [REC YEAR] ! PARSE-BYTE [REC MONTH] C! PARSE-BYTE [REC DAY] C! PARSE-BYTE [REC HOUR] C! PARSE-BYTE [REC MINS] C! PARSE-BYTE [REC DURATION] ! PARSE-BYTE [REC FLAG] C! PARSE-INT [REC YEAR2] ! PARSE-BYTE [REC MONTH2] C! PARSE-BYTE [REC DAY2] C! ; : )EVENT UPDATE FLUSH \ mark block updated and flush to disk ; \ advance to next record : DATE@ ( -- day month year) [REC DAY] C@ [REC MONTH] C@ [REC YEAR] @ ; : END@ ( -- day month year) [REC DAY2] C@ [REC MONTH2] C@ [REC YEAR2] @ ; : TIME@ ( -- mins hr ) [REC MINS] C@ [REC HOUR] C@ ; : .YYYYMMDD ( day month year --) 4 .R ." /" 2 .R ." /" 2 .R ; : .hhmmss ( sec mins hr --) 2 .R ." :" 2 .R ; : .REPEATS ( n --) CASE 0 OF ." No" ENDOF 1 OF ." Daily" ENDOF 2 OF ." Weekly" ENDOF ." ???" ENDCASE ; : PRINT] ( addr -- ) CR ." Name: " [REC EVENT] COUNT TYPE 4 SPACES ." Date: " DATE@ .YYYYMMDD CR ." Starts:" TIME@ .hhmmss CR ." Duration:" [REC DURATION] @ 4 .R ." mins" CR ." Repeats: " [REC FLAG] C@ .REPEATS CR ." Ends: " END@ .YYYYMMDD ; : ERASE] B/REC 0 FILL ; : LIST ( -- ) 9 0 DO I REC# [REC PRINT] CR LOOP ; \ ("E1", 2021, 11, 23, 16, 30, 15, 0, 0, 0, 0) \ Since you have defined your event like that I would be temped to make the code \ look like that and drop that data into a record. 0 REC# '( " E1" 2021,11,23,16,30,15,0,0,0,0 )EVENT 1 REC# '( " E2" 2021, 11, 23, 16, 30, 15, 1, 2021, 11, 27 )EVENT 2 REC# '( " E3" 2021, 11, 23, 16, 30, 15, 2, 2021, 12, 15 )EVENT 3 REC# '( " E4" 2021,11,23,16,30,15,0,0,0,0 )EVENT 4 REC# '( " E5" 2021, 11, 23, 16, 30, 15, 1, 2021, 11, 27 )EVENT 5 REC# '( " E6" 2021, 11, 23, 16, 30, 15, 2, 2021, 12, 15 )EVENT 6 REC# '( " E7" 2021,11,23,16,30,15,0,0,0,0 )EVENT 7 REC# '( " E8" 2021, 11, 23, 16, 30, 15, 1, 2021, 11, 27 )EVENT 8 REC# '( " E9" 2021, 11, 23, 16, 30, 15, 2, 2021, 12, 15 )EVENT Edited November 24, 2021 by TheBF typo 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted November 24, 2021 Share Posted November 24, 2021 I asked for some examples of large Forth projects on comp.lang.forth. This one came in that shows Forth working in the layer cake of languages: https://ojs.library.queensu.ca/index.php/PCEEA/article/view/4004 From a quick read: 1. Forth was used to write the CNC interpreter. 2. C was used to write the Forth system called PFE. 3. Python was used to write the GUI So using each tool to its strength. Nice. 2 Quote Link to comment Share on other sites More sharing options...
neglectoru Posted November 24, 2021 Author Share Posted November 24, 2021 Thanks @TheBF for your examples, and for tinkering with my toy problem! I'll definitely check out the examples in more detail. I think you put it well. The Algol-like languages are all very different than Forth, and I think they've influenced (poisoned?) my thinking for much of my adult life. That said, I think Forth's minimalism and low-level nature may contribute to its difficulty to read. In my example of having an event structure, I invented getter methods for the structure, and didn't know about ANS forth structs which solve basically the same problem. Things that are common in other languages (strings, objects, structures, literals) are up to each Forth environment to re-invent. Every C implementation "agrees" on some concepts (structs, pointers, function calls, etc.). The only thing that Forth implementations really agree on is that every word evaluates left to right. Much of the time they operate on the parameter stack (or return stack), some of the time they define new concepts. Forth systems agree on some common vocabulary (:, DUP, SWAP, +, etc.) but then differ widely when you leave this minimalistic set. This means that either your problem should be well suited to the minimalistic set (doing things easier than you would in an assembler, like a device driver), or you need to create your own custom language, or you need to rely on what your particular Forth environment gives you for higher level problems, or you need to build your own Forth interpreter that can address the kinds of problems you want to solve. Since it's so much easier to write a Forth interpreter than a C compiler, that happens a lot. But it does mean that if you make poor choices of what your interpreter should look like, or what your new vocabulary should feel like, you will get yourself into a very deep pit. That's why I wanted to see examples of successful big forth systems, and see what they do right. So thanks again for the examples and the discussion! 5 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted November 24, 2021 Share Posted November 24, 2021 I couldn't have said it better. I'm happy it was helpful. 3 Quote Link to comment Share on other sites More sharing options...
Switch1995 Posted August 27 Share Posted August 27 On 11/22/2021 at 10:25 AM, Vorticon said: Here's the JetPac source written in TurboForth. It is quite involved, so feel free to ask questions. Probably the best game written in forth. Deserves a code talk on your YouTube channel IMHO 1 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.