MY FIRST EXPERIENCES WITH FORTH. This article is based on an article that I published in 1992 in the Dutch Forth magazine "het Vijgeblad" (literally this means "fig leaf", but "blad" also means "magazine"). And everyone knows that FIG means "Forth Interest Group". This magazine was published by the HCC Forth Interesse Groep. I'd like to talk about my first experiences with Forth on the ZX Spectrum. Most people remember the venerable Sinclair ZX Spectrum as a game machine. Although I did play some games on it every now and then, the primary reason I had the spectrum was hobby programming, or "hacking" in the positive sense. But let's start a few years earlier. THE BEGINNING. The first time I read about Forth was in the October 1983 issue of the Dutch magazine "Radio Bulletin", which contained a review of the Jupiter Ace computer. This machine looked a lot like the ZX81, but it had Forth in its ROM instead of BASIC. Forth was fast and compact and it offered structured programming. In 1984 our family purchased a Texas Instruments TI 99/4A. Alas this is not a very "hackable" machine, at least not if you are not a hardware hacker and you cannot afford the extra add-ons like "Extended Basic" or "Mini Memory". Though Forth did exist for that machine, it was not within reach. Problem was, on the bare machine you could not PEEK and POKE and basically there was no way you could enter and run your own machine code programs. The hardware contained all kinds of interesting goodies, like sprites, that you couldn't access. At that time I'd rather own a ZX81 on which you did have the opportunity to hack with machine code. In 1985 I started my study at the Eindhoven University of Technology and though I was a poor student, I could afford the 299 guilders (say 150 dollars) that a 48k ZX Spectrum cost. I had to use an "antique" black and white TV and cassette recorder. Though it was an inferior machine hardware-wise and its BASIC was as slow, I could do a lot more with it than with the TI. Spectrum BASIC had some pretty cool features, like high resolution graphics, that TI and even the Commodore 64 lacked. Apart from BASIC you could easily hack your way with PEEK, POKE and machine code. "The Complete Spectrum ROM Disassembly" was the best reference manual you could get. At the end of 1986 I became a bit dissatisfied with the ZX Spectrum. BASIC was way too slow for many purposes and Assembler was so low-level. I couldn't afford Pascal at the moment (I did purchase it later). Forth seemed the way to go. While at the university I did read the occasional Forth book. the book "The Complete Forth" by Alan Winfield got me hooked. But how to get a Forth system? Well I _knew_ someone who had Aackosoft Forth and who could send me a copy, but I even tried to buy it myself. But it turned out it was no longer available. When I did get a copy I was glad I didn't spend good money on it. The editor was very stupid. It turned out that there could be one! 1k block of Forth source in memory at a time and that you had to save and load each source block separately on the cassette. If you edited one block of source, you had to save it back right at the place of the old block on the tape. That was not the way I wanted to edit my source code! The editor had to be loaded from tape too. Worst of all, you couldn't simply save the image of your Forth system with some precompiled extensions (the editor). After some time I did manage to hack my way out of that, but by then I had already decided to roll my own Forth. WRITING MY OWN FORTH Forthunately there were a lot of Forth books in the university library, mostly related to FIG Forth. Aackosoft Forth itself was also a FIG Forth and I had laid my hands on a book about Abersoft Forth, a competing Forth for the Spectrum that stored 10 blocks into a RAM disk that you could save as a whole. I definitely wanted a '79 Forth as described by Winfield, a RAM disk and a full-screen editor. Some other goodies I wanted: Full 32-bit number capability and a BREAK key to stop an endless loop without having to load all the stuff again from cassette. At that time I pretty much knew what a Forth system looked like internally and what made it tick, but I didn't know about metacompiling (for which you need a working Forth to begin with). Neither did I want to build the whole thing in assembler like the Fig Forth listings. So I started to write the machine code primitives in assembler. Below you see a small piece of the assembler source, containing the inner interpreter itself (NEXT) and a few primitives. Note that the address of the NEXT routine is kept in the IX register. This Idea I got from "Threaded Interpretive Languages" by Loeliger. BTW: Loeliger was a _real_ programmer. He managed to hack his Forth together in a week without using any tools other than peek and poke. NEXT LD A,(BC) LD L,A INC BC LD A,(BC) LD H,A INC BC EXE LD E,(HL) INC HL LD D,(HL) EX DE,HL JP (HL) EXECUTE POP HL JR EXE COLON LD HL,(RSP) DEC HL LD (HL),B DEC HL LD (HL),C LD (RSP),HL INC DE LD B,D LD C,E JP (IX) EXIT LD HL,(RSP) LD C,(HL) INC HL LD B,(HL) INC HL LD (RSP),HL JP (IX) The assembler source compiled to a piece of machine code of around 1 kilobyte. It contained the code of all primitives, including double precision multiplication and division. It contained no headers. When this piece of code was assembled, I made a note of all the relevant label addresses. This Forth was located at a comparatively high address (9000h IIRC). At the lower addresses there would be room for the BASIC program that I describe next. When the Forth was finished, the same space would be occupied by the RAM disk. The rest of the Forth system was compiled using a BASIC program, which was loaded and run in more parts. The BASIC program built up all the headers. The first part compiled the headers for the primitives and the variables and constants. Subsequent parts compiled the colon definitions. The program contained a BASIC subroutine that would search the Forth dictionary in memory just like the Forth system would. Below you see this FIND routine. It needs the name in A$ and returns the address in FA. 1090 LET FA=LK 1091 LET A=PEEK FA-128: IF A>=64 THEN LET A=A-64 1095 IF A=LEN A$ THEN FOR I=1 TO LEN A$: IF PEEK (FA+I)=CODE A$(I) THEN NEXT I: GO TO 1120 1100 LET FA=FA+A+1: LET FA=PEEK FA+256*PEEK (FA+1): IF FA=0 THEN PRINT "FOUT ";A$: GO TO 9000 1110 GO TO 1091 1120 LET FA=FA+A+3: RETURN The compiler was indeed very primitive. It contained no words like IF and BEGIN and you had to manually compute branch offsets. The Forth words were contained in DATA lines, like this one. 3130 DATA "MEM", "LIT", "&L9005", "@", "DEPTH", "-", "DEPTH", "-", "HERE", "-", "LIT", "&L0080", "-", "EXIT", "?STACK", "DEPTH", "0<", "0BRANCH", "&B16", "(."")", "&SSTACK EMPTY", "ERROR", "MEM", "0<", "0BRANCH", "&B15", "(."")", "&SSTACK FULL", "ERROR", "EXIT" In normal Forth these definitions would look like : MEM ( --- size) ( return amount of free memory) 9005 @ DEPTH - DEPTH - HERE - 80 - ; : ?STACK ( --- ) ( check the stack pointer) DEPTH 0< IF ." STACK EMPTY" ERROR THEN MEM 0< IF ." STACK FULL" ERROR THEN ; Needless to say this BASIC program was SLOW! Compilation of the complete system took nearly one hour! Most bugs turned out to be branch offsets. About half January 1987 I started and by the end of February the whole thing was finished. The Forth that I built using the BASIC program was not very complete, but complete enough to compile the rest of itself. Words like IF and BEGIN were among the first that needed compilation. No editor? You could enter the code using 1 BLOCK 704 EXPECT. Making the Forth system complete and writing the editor took relatively very little time. The resulting Forth system had a RAM disk of 16 blocks of 704 bytes each. Why 704 bytes? Those fitted nicely onto a screen of 32 columns and 24 lines (22 lines in use for the actual text). It contained a full-screen editor (primitive indeed by today's standards) and it fit my requirements exactly. It was a compact program, about 8kB. Of course it had a few peculiarities: The word VOCABULARY was very buggy and CREATE reserved two extra bytes (for the DOES> pointer) like BODY ! ) Scr#1 \ METAFORTH83 INNER INTERPRETER HEX ASSEMBLER 0 JP 0 JP 0 JP FORTH DEFINITIONS HERE-T CONSTANT 'BASE HERE-T 2 + CONSTANT 'DP HERE-T 4 + CONSTANT 'KEY HERE-T 6 + CONSTANT 'EMIT HERE-T 8 + CONSTANT RPTR HERE-T A + CONSTANT 'WAIT HERE-T C + CONSTANT 'FENCE HERE-T E + CONSTANT 'S0 HERE-T 10 + CONSTANT 'R0 ORIGIN 2- CONSTANT UPTR 3C ALLOT-T VARIABLE 'I ASSEMBLER DEFINITIONS : JPIX %X X JPHL ; ( INNER INTERPRETER) HERE-T ' NEXT >BODY ! EXDE M E LD H INC M D LD H INC EXDE JPHL HERE-T ' DOCON >BODY ! H POP M C LD H INC M B LD B PUSH JPIX HERE-T ' DOCOL >BODY ! RPTR LDHL H DEC D M LD H DEC E M LD RPTR STHL D POP JPIX HERE-T ' DOUSER >BODY ! H POP M C LD 0 B LD# UPTR LDHL B ADDP H PUSH JPIX --> Scr#2 \ META PRIMITIVES LIT,BRANCH,DO META DEFINITIONS CODE LIT HERE-T 'LIT ! EXDE M C LD H INC M B LD H INC B PUSH NEXT 1+ JP ;C CODE BRANCH HERE-T 'BRANCH ! EXDE M E LD H INC M D LD JPIX ; C CODE ?BRANCH HERE-T '?BR ! B POP B A LD C OR 'BRANCH @ JP Z D INC D INC JPIX ;C CODE EXECUTE H POP JPHL ;C CODE EXIT HERE-T 'EXIT ! RPTR LDHL M E LD H INC M D LD H INC RPTR STHL JPIX ;C CODE (DO) HERE-T 'DO ! H POP B POP H PUSH RPTR LDHL H D EC B M LD H DEC C M LD D INC D I NC H DEC D M LD H DEC E M LD EXS P A AND B SBCP H A LD 80 XOR# A B LD L C LD H POP H DEC B M LD H DEC C M LD RPTR STHL JPIX ;C CODE (?DO) HERE-T '?DO ! H POP B POP B SUBP NZ IF B ADDP B PUSH H PUSH 'DO @ JR THEN 'BRANCH @ JP ;C --> I wrote a complete manual for it (in Dutch) using the Forth editor. I printed this on a 32 column thermal printer. Next I cut the thermal paper and pasted it to A4 sheets (in two columns). These I could photocopy. I wrote the following extensions to my Forth. - Full double precision words. - A floating point package including transcendental functions (not using the slow ROM routines). - Graphics and sound routines. - A decompiler. - Words to handle strings and other data types. - A primitive multitasker! I documented all of these in the manual. I wrote The following Forth applications. - A user defined graphics editor. - A logic simulator. - A very primitive chess program. - A turtle graphics, Logo, screen drawing package. Later I printed the entire manual in two columns on Coos Haak's matrix printer and printed it 12 times. (matrix printing was cheaper than Xeroxing.) At the "HCC days" (November 1988) several cassettes and manuals were sold. THE PC AND BEYOND. In August 1988 I purchased a PC clone. After all I really needed an 80 column screen and real disks for text processing. Forth continued to be the primary language used, especially when I got F-PC in early 1989 (I had F83 before). One of the things I wrote in it was a BASIC interpreter with all the floating point and string functions you would expect on the old home computers. Another program was a complete 6809 simulator/assembler/disassembler. In 1991 I got hold of a Spectrum emulator running on PC (It was called Z80 and I actually registered that shareware package). I wrote a primitive tape loading program for the PC (the emulator didn't have this function back then) and someone transferred some of my files to diskettes, for which I also made a program to read them on a PC. With those tools I got most of the Spectrum stuff transferred to the PC. Thanks to this effort you can now download forth83.zip from ftp.nvg.unit.no. In 1992 I purchased a 386 and I moved to Linux. While there are Forths under Linux and I even contributed to bug fixing, Forth was no longer my primary language. I continued to work on the 6809 emulator and BASIC in Forth. In Linux with its abstract device files, Forth is very far removed from its original home, hardware control. I did write an instruction set simulator for an imaginary 32-bit processor specially designed to run Forth. Of course I wrote a Forth for it. The 6809 simulator (now rewritten in C) also has Forth running on it. You guess it right: I'm fascinated by emulators and I'm not satisfied until it runs both Forth and Tetris (preferably Dirk Zoller's Tetris for Forth). Of course I can run my Spectrum Forth on xzx under Linux. -- Lennart