+TheBF Posted February 24 Share Posted February 24 54 minutes ago, FarmerPotato said: Oops. I understood VFILL to mean VDP fill and that it went horizontal. Like I'd think of 0 768 32 VFILL to clear the screen. I also found it annoying that vaddr is below len and char. If I'm iterating on vaddr, it requires a nasty 2DUP I -ROT to prepare the stack for FILL/VFILL. I imagine a ! order: : VFILL! ( char len vaddr -- ) SETVWA 0 DO DUP VDPWD ! LOOP ; You are correct. I was trying to say that where HCHAR uses x,y VFILL uses a VDP address as a starting point. Typically VFILL like FILL is a code word so we can access args randomly. But if I had to do VFILL in Forth I might do this: : VFILL ( Vaddr len char -- ) -ROT ( char vaddr len) OVER + SWAP ( char Vend Vstart) DO DUP I C! LOOP DROP ; Your version would work faster with SETWA defined and using the -ROT (reverse rotate) operator. I actually did it this way in my "RECOMPILER" as an experiment. Check out this driver in Forth. (FOR NEXT is a simple down counter. Way less code than DO/LOOP. https://github.com/bfox9900/RECOMPILER/blob/main/src/LIB/VDPDRVR-FORNEXT.FTH See here for my ALC version use in Camell99 Forth https://github.com/bfox9900/CAMEL99-ITC/blob/master/Compiler/cc9900/SRC.ITC/TI99IOX.HSF 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5416986 Share on other sites More sharing options...
+TheBF Posted March 5 Share Posted March 5 Still working on cleaning up my repository and I came across the old sevens problem. I always wanted a version that was a literal translation of the BASIC so I did one. It's fun to look at. I did not use any tricks unless you call one DUP a trick. Here is the TI BASIC version that runs in 27 minutes. I am afraid I have lost the accreditation for it so speak up if it's yours. (Lucien?) Edit: Original program was written by @unhuman. I modified lines 340 to 360 to speed up printing. The original used HCHAR. Spoiler 1 rem for comparison to Forth 2 rem TI BASIC version runs in 27mins and 20 seconds 10 OPEN #1:"CLOCK" 20 INPUT #1:A$,DATE$,START$ 100 DIM A(256) 110 PRINT "7's Problem" 120 A(1)=7 130 WIN=0 140 POWER=1 150 NUMLEN=1 160 POWER=POWER+1 170 PRINT "7 ^";POWER;"IS:" 180 CARRY=0 190 INAROW=0 200 FOR I=1 TO NUMLEN 210 A(I)=A(I)*7+CARRY 220 CARRY=INT(A(I)/10) 230 A(I)=A(I)-CARRY*10 240 IF A(I)<>7 THEN 290 250 INAROW=INAROW+1 260 IF INAROW<>6 THEN 300 270 WIN=1 280 GOTO 300 290 INAROW=0 300 NEXT I 310 A(I)=CARRY 320 IF CARRY=0 THEN 340 330 NUMLEN=NUMLEN+1 340 FOR I=NUMLEN TO 1 STEP -1 350 PRINT CHR$(A(I)+48); 360 NEXT I 363 REM we're done get the time 365 INPUT #1:A$,DATE$,END$ 370 PRINT :: 380 IF WIN<>1 THEN 160 390 PRINT "WINNER IS 7 ^";POWER 394 Print "Sevens started:";START$ 400 PRINT "Ended at ";END$ 410 CLOSE #1 420 END And here is a Forth literal translation that runs in edit: 1 minute 10 seconds on my indirect threaded system. ( Re-timed using a stop-watch because the there was too much VDP I/O playing with the ISR timer) I have included the BASIC code as comments. It would be faster if I replace the VARIABLEs with VALUEs but this is vanilla Forth. Most systems have character array creator CARRAY or it can be made easily enough. Spoiler \ literal translation of BASIC program to Forth INCLUDE DSK1.TOOLS INCLUDE DSK1.ELAPSE INCLUDE DSK1.ARRAYS DECIMAL : ?BREAK ?TERMINAL ABORT" *BREAK*" ; \ must define all data before use VARIABLE WIN VARIABLE POWER VARIABLE NUMLEN VARIABLE CARRY VARIABLE INAROW VARIABLE NDX ( transfers loop index out of DO LOOP ) 256 CARRAY ]A \ 100 DIM A(256) : RUN CR ." 7's Problem " \ 110 PRINT "7's Problem" 0 ]A 256 0 FILL \ init ]A to zero 7 0 ]A C! \ 120 A(1)=7 WIN OFF \ 130 WIN=0 1 POWER ! \ 140 POWER=1 0 NUMLEN ! \ 150 NUMLEN=1 BEGIN POWER 1+! \ 160 POWER=POWER+1 ." 7 ^" POWER @ . ." IS:" \ 170 PRINT "7 ^";POWER;"IS:" ?BREAK CARRY OFF \ 180 CARRY=0 INAROW OFF \ 190 INAROW=0 NUMLEN @ 1+ 0 \ 200 FOR I=1 TO NUMLEN DO I NDX ! \ copy I for later I ]A C@ 7 * CARRY @ + \ 210 A(I)=A(I)*7+CARRY \ We avoid some math with divide & mod function 0 10 UM/MOD CARRY ! \ 220 CARRY=INT(A(I)/10) I ]A C! \ 230 A(I)=A(I)-CARRY*10 I ]A C@ 7 = \ 240 IF A(I)<>7 THEN 290 IF INAROW 1+! \ 250 INAROW=INAROW+1 INAROW @ 6 = \ 260 IF INAROW<>6 THEN 300 IF WIN ON \ 270 WIN=1 LEAVE THEN ELSE \ 280 GOTO 300 INAROW OFF \ 290 INAROW=0 THEN ?BREAK LOOP \ 300 NEXT I CARRY @ DUP NDX @ 1+ ]A C! \ 310 A(I)=CARRY IF \ 320 IF CARRY=0 THEN 340 NUMLEN 1+! \ 330 NUMLEN=NUMLEN+1 THEN CR \ replaces PRINT 0 NUMLEN @ \ 340 FOR I=NUMLEN TO 1 DO I ]A C@ >DIGIT (EMIT) \ 350 PRINT CHR$(A(I)+48); -1 +LOOP \ 360 NEXT I ( STEP -1) CR CR \ 370 PRINT :: WIN @ \ 380 IF WIN<>1 UNTIL \ THEN 160 ." Winner is 7 ^" POWER @ . \ 390 PRINT "WINNER IS 7 ^";POWER ; \ 420 END 3 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5423321 Share on other sites More sharing options...
+Lee Stewart Posted March 5 Share Posted March 5 10 hours ago, TheBF said: Here is the TI BASIC version that runs in 27 minutes. I am afraid I have lost the accreditation for it so speak up if it's yours. (Lucien?) Could be @lucien2, but check the first few posts of @Willsy opened with saying he had coded a TI Basic version, but I don’t know whether he ever posted it. ...lee 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5423494 Share on other sites More sharing options...
+TheBF Posted March 5 Share Posted March 5 I just followed the early thread and the program I posted was written by @unhuman I will add that to the post. Thanks for making me do my homework professor. Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5423502 Share on other sites More sharing options...
+TheBF Posted March 5 Share Posted March 5 When I reviewed the thread on this program I found the results of this program compiled with the BASIC compiler. On 3/24/2011 at 8:59 PM, unhuman said: Compiled it kicks Forth's butt! "Maybe versus Weiand's Forth: Compiled-Basic: 1m40s Weiand-Forth: 3m" But the Weiand Forth version was written by Lucien who admitted that he was new to Forth and the algorithm is completely different. Now, with a line by line translation, we can see that indirect-threaded Forth and compiled BASIC are running about the same speed. If we compiled my BASIC version with simpler printing it would probably take the extra 20 seconds off or maybe more. @Lee Stewart wrote a version using Forth idiomatically at it runs on my system in 1:02 with the default scroll code. If increase the scroll buffer to the entire screen it runs in 55 seconds, but that's wasteful. LOL. I am moving over to the old Seven's problem post. I wonder if my JIT can swallow the new version... ? Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5423513 Share on other sites More sharing options...
Willsy Posted March 5 Share Posted March 5 I'm pretty sure the original source for this is a magazine article. I'm 90% sure it would have been a TI*MES article since I'm a Brit and us Brit 99ers were fed on a diet of TI*MES back in the day. Stephen Shaw will probably remember. I can't remember his handle on this forum. Yeah, I was pretty impressed with the Forth cut, until @sometimes99er did a version in assembly which blew everything away! Doh! 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5423565 Share on other sites More sharing options...
+Lee Stewart Posted March 5 Share Posted March 5 2 hours ago, Willsy said: I'm pretty sure the original source for this is a magazine article. I'm 90% sure it would have been a TI*MES article since I'm a Brit and us Brit 99ers were fed on a diet of TI*MES back in the day. Stephen Shaw will probably remember. I can't remember his handle on this forum. Yeah, I was pretty impressed with the Forth cut, until @sometimes99er did a version in assembly which blew everything away! Doh! Stephen Shaw is @blackbox here. ...lee 2 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5423666 Share on other sites More sharing options...
+TheBF Posted May 9 Share Posted May 9 Cleaning up I found and fixed this experiment. I have not used it in a project but if you ever needed a hardware timer ... I think it would port to the other Forth systems without much trouble. ( SUB, would need to change to S, Change the NEEDS line to conditionally load the assembler in your system of choice If you don't have ?DO change it to 1 MAX DO for some protection. ) It consumes 86 bytes of your dictionary but gives you access to the 9901 internal timer. Convert it to machine code to remove the need for the assembler \ hardtime.fth very un-cooperative but accurate NEEDS MOV, FROM DSK1.ASM9900 HERE DECIMAL CREATE _TMR ( -- ) \ read the TMS9901 timer 0 LIMI, R12 2 LI, \ cru address for timer -1 SBO, \ SET bit 0 TO 1, Enter timer mode R0 14 STCR, \ read timer (14 bits) -1 SBZ, \ reset bit 1, exit timer mode RT, CODE 1MS ( -- ) _TMR @@ BL, \ read time to R0 R0 R1 MOV, \ dup time into R1 BEGIN, _TMR @@ BL, \ read time again R1 R0 SUB, \ subtract from dupped read R0 ABS, R0 46 CI, \ compare to 46. ~= 1000uS GTE UNTIL, 2 LIMI, NEXT, ENDCODE : HARDMS ( n -- ) 0 ?DO 1MS LOOP ; HERE SWAP - . .( bytes) 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5463354 Share on other sites More sharing options...
+Vorticon Posted May 9 Share Posted May 9 Cool. This could be useful. What is the resolution and what is the longest interval possible? Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5463369 Share on other sites More sharing options...
+TheBF Posted May 9 Share Posted May 9 (edited) The timer has a maximum value of >3FFF (16383) x 21.3uS = 348957.9 uS = 349 mILLIseconds Not very much. So I put the timer reading in a loop to make 1mS which is a reasonable time for TI-99. Then by putting 1MS in a hi-level loop you can do a delay as long as your loop can spin. Forgot to answer: This version has a resolution of 1mS. If you wanted shorter time change the value 46 to 23 to get .5 mS for example. It can get wonky if you go too low because the 9900 is not that fast at looping compared to the time you want. Edited May 9 by TheBF typo 2 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5463374 Share on other sites More sharing options...
+TheBF Posted May 9 Share Posted May 9 @Vorticon I may have lead you astray in that I leave the 9901 timer running continuously and I didn't show you that code. Here is how I set the timer to specific number. ( TOS is R4 aliased) After this function runs the timer is down-counting forever. Then you can read it anytime you need it. I found this timer was a bit fast for polling in Forth so that would be true in PASCAL as well but you could make something that loads the timer with 46 or so and wait for it to expire. That would be 1 milli-second as well. (Never tried it this way.) CODE TMR! ( n -- ) \ load TMS9901 timer from stack 0 LIMI, R12 CLR, \ CRU addr of TMS9901 = 0 0 SBO, \ SET bit 0 to 1, Enter timer mode R12 INCT, \ CRU Address of bit 1 = 2 , I'm not kidding TOS 14 LDCR, \ Load 14 BITs from TOS into timer -1 SBZ, \ reset bit 0, Exits clock mode, starts decrementer 2 LIMI, TOS POP, NEXT, ENDCODE 2 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5463515 Share on other sites More sharing options...
+TheBF Posted June 1 Share Posted June 1 Just a little lesson for the Forth student on making Forth run faster. The Assembler is Camel Forth specific but the concepts are applicable to any Forth system. While studying TI-Forth for some insights into Graphics2 mode I found this code that initializes the image table in VDP RAM. It needs to fill HEX 300 bytes of VDP memory with sequential numbers bytes, so 0 .. FF over and over 3 times. : TI-INIT -1 1B00 1800 DO 1+ DUP 0FF AND I VSBW LOOP DROP ; Timings are for running these routines 256 times. Equivalent to 70,200 byte writes to VDP RAM. The TI Forth version ran in 82 seconds. It seemed a little verbose because VSBW writes a byte and so it doesn't need 0FF AND in the loop to mask any number that's too big. Instead of using a start and end address I use the start address and >300 to feed BOUNDS which converts (address,size) to (end-address,start-address) for me. That's a tiny bit slower but harder to make a mistake with in my opinion. I start with 0 on the data stack, write it to VDP RAM and then increment it with 1+ which seem less confusing than starting on -1. I re-wrote it like this and it ran in 64.2 seconds, 28% faster, using a stopwatch since an interrupt driven timer might be wrong with all this VDP action. Anytime you can remove stuff from an inner loop in Forth speed improves nicely. 1B00 CONSTANT IMG : INIT2 ( -- ) 0 IMG 300 BOUNDS DO DUP I VC! 1+ LOOP DROP ; Reminder VC! is just VSBW under a different name and it has to turn off interrupts and set the VDP address before every byte that it writes. Not ideal since the VDP chip increments that address by itself after every write. So... How about we fix that? You have to turn interrupts off when writing to VDP so a little CODE word was made to do that. I tried writing to the VDP port using a Forth word and there was a 10% improvement at 57 seconds. But it's only 3 instructions to do a VDP byte write in Forth Assembler and that dropped the time to 33 seconds, 48% faster than the original code. If we use VC! on the first byte, the VDP address is set and ready to go for any future writes. \ Turn interrupts off in Forth CODE 0LIMI 0 LIMI, NEXT, ENDCODE \ : VC!++ 8C00 C! ; CODE VC!++ TOS SWPB, TOS 8C00 @@ MOV, TOS POP, NEXT, ENDCODE : INIT3 0 IMG VC! \ write first byte, which sets VDP write address 0LIMI \ turn off interrupts while we loop. \ now we just write the loop index into VDP memory with VC!++ 300 1 DO I VC!++ LOOP ; \ 57 secs Forth VC!++, 33 secs. with ALC 2 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5478017 Share on other sites More sharing options...
+TheBF Posted June 7 Share Posted June 7 Looking through my "demo" code "work in progress" I found a copy of mergesort that I had copied from Rossetta code. DECIMAL \ camel99 forth translation harness : RDROP POSTPONE R> POSTPONE DROP ; IMMEDIATE : <= POSTPONE 1+ POSTPONE < ; IMMEDIATE 1 CELLS CONSTANT CELL : MERGE-STEP ( right mid left -- right mid+ left+ ) OVER @ OVER @ < IF OVER @ >R 2DUP - OVER DUP CELL+ ROT MOVE R> OVER ! >R CELL+ 2DUP = IF RDROP DUP ELSE R> THEN THEN CELL+ ; : MERGE ( right mid left -- right left ) DUP >R BEGIN 2DUP > WHILE MERGE-STEP REPEAT 2DROP R> ; : MID ( l r -- mid ) OVER - 2/ CELL NEGATE AND + ; : MERGESORT ( right left -- right left ) 2DUP CELL+ <= IF EXIT THEN SWAP 2DUP MID RECURSE ROT RECURSE MERGE ; : MSORT ( addr len -- ) CELLS BOUNDS MERGESORT 2DROP ; I wondered how it would compete with quicksort. The spoiler is: Not as well as I thought it would. (this is an "in place" mergesort so probably it would benefit from a secondary buffer. On a 1,000 item array of integers, reversed, this mergesort took 41 seconds. Quick sort was 4.4!! I could not remember how my "teacher's pet" combsort did so I pulled it up and of course realized that it was not as general purpose as these other sorts. The correct way to do these things is to pass the array address and a size on the data stack. I had made a test version that had the array name hard coded in the sort. So I fixed combsort to behave correctly and I changed the "exchange" code to work on addresses. Same test, combsort came it at 18.4 seconds. Not too shabby for a glorified bubble-sort! Here is the updated combsort with timings for all the different tests. Combsort doesn't vary much with different data. (Quicksort's worst case was all values the same. It took 8.18 seconds to complete that one so almost 2X) It should not be too hard to make a bit of "harness" to use this in FbForth or TurboForth. If you need help in that area, just holler. Spoiler \ combsort for the non-forth programmer INCLUDE DSK1.TOOLS INCLUDE DSK1.RANDOM INCLUDE DSK1.MALLOC INCLUDE DSK1.ELAPSE NEEDS VALUE FROM DSK1.VALUES HEX \ gratuitous variables for clarity 0 VALUE GAP VARIABLE SORTED \ divide by 1.35 using Forth's scaling operator \ found this ratio to be the fastest : 1.35/ ( n -- n' ) 100 135 */ ; : XCHG ( addr1 addr2 -- ) 2DUP @ SWAP @ ROT ! SWAP ! ; : COMBSORT ( addr n -- ) DUP>R TO GAP \ save n on Rstack and in GAP BEGIN GAP 1.35/ TO GAP \ re-compute the gap SORTED ON R@ ( -- n) GAP - 0 \ n-gap is loop limit DO ( -- addr ) DUP I CELLS + DUP GAP CELLS + ( -- addr1 addr2 ) OVER @ OVER @ > \ compare the contents of cells IF XCHG \ Exchange the data in the cells SORTED OFF \ flag we are not sorted ELSE 2DROP THEN LOOP SORTED @ GAP 0= AND \ test for complete UNTIL R> 2DROP ; \ ============ TESTING COMMANDS ====================== \ load the array with different kinds of mixed up data HEX 2000 H ! \ reset the heap DECIMAL 1000 CONSTANT SIZE SIZE CELLS MALLOC CONSTANT Q[] \ macro is a bit faster : ]Q S" CELLS Q[] + " EVALUATE ; IMMEDIATE : ERASE 0 FILL ; : CLEARIT ( -- ) 0 ]Q SIZE CELLS ERASE ; \ all the same data 17.7 : REVERSED ( -- ) SIZE 0 DO SIZE I - I ]Q ! LOOP ; \ 18.0 seconds : ASCENDING ( -- ) SIZE 0 DO I I ]Q ! LOOP ; \ 17.7 : RANDIT ( -- ) SIZE 0 DO 256 RND I ]Q ! LOOP ; \ 19.6 : TWOIT ( -- ) CLEARIT 99 45 ]Q ! 777 SIZE 2/ ]Q ! ; \ 2 different records 17.7 : TURTLES ( -- ) \ 18.0 SIZE 0 DO I I CHAR+ ]Q ! \ each 2 elements are reversed I CHAR+ I ]Q ! 2 +LOOP ; \ use this to print the array (use FNCT 4 to break out (ALT 4 on PC) : .Q ( -- ) CR SIZE 0 DO I ]Q @ U. ?BREAK LOOP ; : GO Q[] SIZE COMBSORT ; 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5481552 Share on other sites More sharing options...
+TheBF Posted June 10 Share Posted June 10 (edited) Constant Folding in Forth ("Armstrong" Method. ie: do it yourself) While pondering how to add a compiler feature called "constant folding" I realized that Forth allows the programmer to do this under programmer control. It was not obvious to me when I started using Forth that this was available or even desirable so here is some info for those new to Forth. "Constant folding" in compilers lets the compiler search for expressions with numbers or constants that have some kind of math operation applied to them. For example: : EXAMPLE X 7 + ; If X is a constant, adding 7 to it never changes. We could perform the math when the program compiles and put the final answer into the program as a number instead of computing it every time. Easy to understand why that is beneficial. The trick is to make the compiler understand when to do this and when NOT to do this. Forth "compilers" are typically very simple beasts. Chuck believed he knew better than a computer what should be in the program. So *traditional Forth has no automatic constant folding but there tools that let us do it ourselves. (* Mecrisp Forth and VFX Forth have automatic constant folding but they are, you know, from the 21st century) The word LITERAL lets us compile a literal number into a program. Normally we let the Forth compiler do this for us automatically when we write a word like this: : GREATCPU 9900 ; If we decompile the word GREATCPU we see something like this: SEE GREATCPU : GREATCPU LIT 9900 ; What is LIT? LIT is a little Forth word that does this when it runs: Get the contents of the next memory cell Push the content it got (a number) onto the data stack How did LIT get there? The Forth compiler saw a "literal" number in our program and it knows that it needs to compile the LIT word first to handle the number properly. The next tool we need is the Forth word [ What? Not obvious but that little "word" turns off the compiler, meaning the interpreter is activated. And then we will need this word ] which turns the compiler on. So here is how we could improve our EXAMPLE program: : EXAMPLE2 [ X 7 + ] LITERAL ; Step by step analysis of EXAMPLE2: colon compiles a new name in the dictionary like normal [ turns off the compiler so the interpreter is running now X is a constant so when interpreted, it puts its value on the data stack 7 is a number so it goes onto the data stack too + adds the two numbers on the data stack and leaves the answer on the data stack ] turns the compiler back on LITERAL is an IMMEDIATE word that compiles LIT into a definition and compiles a number from the data stack into memory (right after LIT) ; compiles EXIT to end the definition and then it runs [ to turn off the compiler Read that again if it was not clear, because this is some Forth weirdness here. So you can see that we could put any amount of computation between [ ] in our definition and melt it all down to one number for LITERAL to handle. I thought of a way to make it look a bit more obvious by adding this word: For ANS Forth (Camel99, GForth) : ]FOLD ] POSTPONE LITERAL ; IMMEDIATE For FigForth/Forth83 (FbForth, TurboForth) : ]FOLD ] [COMPILE] LITERAL ; IMMEDIATE And then it looks like this: : BIGEXAMPLE [ X Y + 8 / 12 * ]FOLD ; And that entire expression is dissolved into one number. *MAKE SURE X AND Y ARE NOT VARIABLES* Edited June 10 by TheBF added item 8. 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5483759 Share on other sites More sharing options...
+TheBF Posted June 21 Share Posted June 21 (edited) Getting cool fonts into Forth from source code. So the BASIC and Assembler programmers have this amazing resource in the form of the Magellan. It gives you ready to go source code that you can drop into your program. What's a Forth programmer supposed to do? Here is method that works by using search and replace on Assembler program data and taking advantage of the Forth memory management words. You can import a font from the GIF files in the attached ZIP file. With your chosen font in Magellan, select Export Assembly DATA to get the dialog box below. You want to select only the "Include Character Data" option. We don't need anything else. Press Export and enter the file name for this font. It will be a .A99 file because it is for Assembly language. The file will look like this: Spoiler PAT0 DATA >0000,>0000,>0000,>0000 PAT1 DATA >0000,>0000,>0000,>0000 PAT2 DATA >0000,>0000,>0000,>0000 PAT3 DATA >0000,>0000,>0000,>0000 PAT4 DATA >0000,>0000,>0000,>0000 PAT5 DATA >0000,>0000,>0000,>0000 PAT6 DATA >0000,>0000,>0000,>0000 PAT7 DATA >0000,>0000,>0000,>0000 PAT8 DATA >0000,>0000,>0000,>0000 PAT9 DATA >0000,>0000,>0000,>0000 PAT10 DATA >0000,>0000,>0000,>0000 PAT11 DATA >0000,>0000,>0000,>0000 PAT12 DATA >0000,>0000,>0000,>0000 PAT13 DATA >0000,>0000,>0000,>0000 PAT14 DATA >0000,>0000,>0000,>0000 PAT15 DATA >0000,>0000,>0000,>0000 PAT16 DATA >0000,>0000,>0000,>0000 PAT17 DATA >0000,>0000,>0000,>0000 PAT18 DATA >0000,>0000,>0000,>0000 PAT19 DATA >0000,>0000,>0000,>0000 PAT20 DATA >0000,>0000,>0000,>0000 PAT21 DATA >0000,>0000,>0000,>0000 PAT22 DATA >0000,>0000,>0000,>0000 PAT23 DATA >0000,>0000,>0000,>0000 PAT24 DATA >0000,>0000,>0000,>0000 PAT25 DATA >0000,>0000,>0000,>0000 PAT26 DATA >0000,>0000,>0000,>0000 PAT27 DATA >0000,>0000,>0000,>0000 PAT28 DATA >0000,>0000,>0000,>0000 PAT29 DATA >0000,>0000,>0000,>0000 PAT30 DATA >0000,>0000,>0000,>0000 PAT31 DATA >0000,>0000,>0000,>0000 PAT32 DATA >0000,>0000,>0000,>0000 PAT33 DATA >1010,>1010,>0000,>1000 PAT34 DATA >2828,>2800,>0000,>0000 PAT35 DATA >2828,>7C28,>7C28,>2800 PAT36 DATA >103C,>5038,>1478,>1000 PAT37 DATA >6464,>0810,>204C,>4C00 PAT38 DATA >2050,>5020,>5448,>3400 PAT39 DATA >3010,>2000,>0000,>0000 PAT40 DATA >0810,>2020,>2010,>0800 PAT41 DATA >2010,>0808,>0810,>2000 PAT42 DATA >0054,>387C,>3854,>0000 PAT43 DATA >0010,>107C,>1010,>0000 PAT44 DATA >0000,>0030,>3010,>2000 PAT45 DATA >0000,>007C,>0000,>0000 PAT46 DATA >0000,>0000,>3030,>0000 PAT47 DATA >0404,>0810,>2040,>4000 PAT48 DATA >1028,>4444,>4428,>1000 PAT49 DATA >1030,>1010,>1010,>3800 PAT50 DATA >3844,>0408,>1020,>7C00 PAT51 DATA >7C08,>1008,>0444,>3800 PAT52 DATA >0818,>2848,>487C,>0800 PAT53 DATA >7C40,>7804,>0444,>3800 PAT54 DATA >3840,>4078,>4444,>3800 PAT55 DATA >7C04,>0408,>1020,>2000 PAT56 DATA >3844,>4438,>4444,>3800 PAT57 DATA >3844,>443C,>0404,>3800 PAT58 DATA >0000,>3030,>0030,>3000 PAT59 DATA >0030,>3000,>3030,>1020 PAT60 DATA >0810,>2040,>2010,>0800 PAT61 DATA >0000,>7C00,>7C00,>0000 PAT62 DATA >2010,>0804,>0810,>2000 PAT63 DATA >3844,>0408,>1000,>1000 PAT64 DATA >3844,>5C54,>5C40,>3C00 PAT65 DATA >1028,>4444,>7C44,>4400 PAT66 DATA >7844,>4478,>4444,>7800 PAT67 DATA >3844,>4040,>4044,>3800 PAT68 DATA >7844,>4444,>4444,>7800 PAT69 DATA >7C40,>4078,>4040,>7C00 PAT70 DATA >7C40,>4078,>4040,>4000 PAT71 DATA >3844,>405C,>4444,>3800 PAT72 DATA >4444,>447C,>4444,>4400 PAT73 DATA >3810,>1010,>1010,>3800 PAT74 DATA >3C08,>0808,>4848,>3000 PAT75 DATA >4448,>5060,>5048,>4400 PAT76 DATA >4040,>4040,>4040,>7C00 PAT77 DATA >446C,>5454,>4444,>4400 PAT78 DATA >4444,>6454,>4C44,>4400 PAT79 DATA >3844,>4444,>4444,>3800 PAT80 DATA >7844,>4478,>4040,>4000 PAT81 DATA >3844,>4444,>5448,>3400 PAT82 DATA >7844,>4478,>5048,>4400 PAT83 DATA >3844,>4038,>0444,>3800 PAT84 DATA >7C10,>1010,>1010,>1000 PAT85 DATA >4444,>4444,>4444,>3800 PAT86 DATA >4444,>4444,>2828,>1000 PAT87 DATA >4444,>4454,>5454,>2800 PAT88 DATA >4444,>2810,>2844,>4400 PAT89 DATA >4444,>2810,>1010,>1000 PAT90 DATA >7C04,>0810,>2040,>7C00 PAT91 DATA >3820,>2020,>2020,>3800 PAT92 DATA >4040,>2010,>0804,>0400 PAT93 DATA >3808,>0808,>0808,>3800 PAT94 DATA >1028,>4400,>0000,>0000 PAT95 DATA >0000,>0000,>0000,>7C00 PAT96 DATA >1810,>0800,>0000,>0000 PAT97 DATA >0000,>3008,>3848,>3C00 PAT98 DATA >4040,>7048,>4848,>7000 PAT99 DATA >0000,>3840,>4040,>3800 PAT100 DATA >0808,>3848,>4848,>3C00 PAT101 DATA >0000,>3844,>7C40,>3C00 PAT102 DATA >1820,>2070,>2020,>2000 PAT103 DATA >0000,>3C44,>3C04,>4438 PAT104 DATA >4040,>7048,>4848,>4800 PAT105 DATA >1000,>3010,>1010,>3800 PAT106 DATA >1000,>3010,>1010,>1060 PAT107 DATA >4040,>4850,>6050,>4800 PAT108 DATA >3010,>1010,>1010,>3800 PAT109 DATA >0000,>2854,>5454,>5400 PAT110 DATA >0000,>7048,>4848,>4800 PAT111 DATA >0000,>3844,>4444,>3800 PAT112 DATA >0000,>7048,>4870,>4040 PAT113 DATA >0000,>1C24,>241C,>0404 PAT114 DATA >0000,>5860,>4040,>4000 PAT115 DATA >0000,>3840,>3804,>7800 PAT116 DATA >2020,>7820,>2020,>1800 PAT117 DATA >0000,>4848,>4848,>3400 PAT118 DATA >0000,>4444,>2810,>1000 PAT119 DATA >0000,>4444,>5454,>2800 PAT120 DATA >0000,>4428,>1028,>4400 PAT121 DATA >0000,>2424,>241C,>0438 PAT122 DATA >0000,>7C08,>1020,>7C00 PAT123 DATA >0C10,>1020,>1010,>0C00 PAT124 DATA >1010,>1010,>1010,>1010 PAT125 DATA >6010,>1008,>1010,>6000 PAT126 DATA >2454,>4800,>0000,>0000 PAT127 DATA >0000,>0000,>0000,>0000 Now we use search and replace on our favourite text editor to do the following: Search for 'PAT' replace with '( ' * needs a trailing space Search for 'DATA' replace with ')' Search for '>' replace with empty string Search for ',' replace with ' , ' at the end of every line type in a space and a comma ' ,' Save the edited version with a .fth extension. And add the extra lines that are in this spoiler. Spoiler \ assembler data converted to Forth format \ loads into VDP RAM at compile time HERE \ remember end of dictionary HEX ( 0 ) 0000 , 0000 , 0000 , 0000 , ( 1 ) 0000 , 0000 , 0000 , 0000 , ( 2 ) 0000 , 0000 , 0000 , 0000 , ( 3 ) 0000 , 0000 , 0000 , 0000 , ( 4 ) 0000 , 0000 , 0000 , 0000 , ( 5 ) 0000 , 0000 , 0000 , 0000 , ( 6 ) 0000 , 0000 , 0000 , 0000 , ( 7 ) 0000 , 0000 , 0000 , 0000 , ( 8 ) 0000 , 0000 , 0000 , 0000 , ( 9 ) 0000 , 0000 , 0000 , 0000 , ( 10 ) 0000 , 0000 , 0000 , 0000 , ( 11 ) 0000 , 0000 , 0000 , 0000 , ( 12 ) 0000 , 0000 , 0000 , 0000 , ( 13 ) 0000 , 0000 , 0000 , 0000 , ( 14 ) 0000 , 0000 , 0000 , 0000 , ( 15 ) 0000 , 0000 , 0000 , 0000 , ( 16 ) 0000 , 0000 , 0000 , 0000 , ( 17 ) 0000 , 0000 , 0000 , 0000 , ( 18 ) 0000 , 0000 , 0000 , 0000 , ( 19 ) 0000 , 0000 , 0000 , 0000 , ( 20 ) 0000 , 0000 , 0000 , 0000 , ( 21 ) 0000 , 0000 , 0000 , 0000 , ( 22 ) 0000 , 0000 , 0000 , 0000 , ( 23 ) 0000 , 0000 , 0000 , 0000 , ( 24 ) 0000 , 0000 , 0000 , 0000 , ( 25 ) 0000 , 0000 , 0000 , 0000 , ( 26 ) 0000 , 0000 , 0000 , 0000 , ( 27 ) 0000 , 0000 , 0000 , 0000 , ( 28 ) 0000 , 0000 , 0000 , 0000 , ( 29 ) 0000 , 0000 , 0000 , 0000 , ( 30 ) 0000 , 0000 , 0000 , 0000 , ( 31 ) 0000 , 0000 , 0000 , 0000 , ( 32 ) 0000 , 0000 , 0000 , 0000 , ( 33 ) 1010 , 1010 , 0000 , 1000 , ( 34 ) 2828 , 2800 , 0000 , 0000 , ( 35 ) 2828 , 7C28 , 7C28 , 2800 , ( 36 ) 103C , 5038 , 1478 , 1000 , ( 37 ) 6464 , 0810 , 204C , 4C00 , ( 38 ) 2050 , 5020 , 5448 , 3400 , ( 39 ) 3010 , 2000 , 0000 , 0000 , ( 40 ) 0810 , 2020 , 2010 , 0800 , ( 41 ) 2010 , 0808 , 0810 , 2000 , ( 42 ) 0054 , 387C , 3854 , 0000 , ( 43 ) 0010 , 107C , 1010 , 0000 , ( 44 ) 0000 , 0030 , 3010 , 2000 , ( 45 ) 0000 , 007C , 0000 , 0000 , ( 46 ) 0000 , 0000 , 3030 , 0000 , ( 47 ) 0404 , 0810 , 2040 , 4000 , ( 48 ) 1028 , 4444 , 4428 , 1000 , ( 49 ) 1030 , 1010 , 1010 , 3800 , ( 50 ) 3844 , 0408 , 1020 , 7C00 , ( 51 ) 7C08 , 1008 , 0444 , 3800 , ( 52 ) 0818 , 2848 , 487C , 0800 , ( 53 ) 7C40 , 7804 , 0444 , 3800 , ( 54 ) 3840 , 4078 , 4444 , 3800 , ( 55 ) 7C04 , 0408 , 1020 , 2000 , ( 56 ) 3844 , 4438 , 4444 , 3800 , ( 57 ) 3844 , 443C , 0404 , 3800 , ( 58 ) 0000 , 3030 , 0030 , 3000 , ( 59 ) 0030 , 3000 , 3030 , 1020 , ( 60 ) 0810 , 2040 , 2010 , 0800 , ( 61 ) 0000 , 7C00 , 7C00 , 0000 , ( 62 ) 2010 , 0804 , 0810 , 2000 , ( 63 ) 3844 , 0408 , 1000 , 1000 , ( 64 ) 3844 , 5C54 , 5C40 , 3C00 , ( 65 ) 1028 , 4444 , 7C44 , 4400 , ( 66 ) 7844 , 4478 , 4444 , 7800 , ( 67 ) 3844 , 4040 , 4044 , 3800 , ( 68 ) 7844 , 4444 , 4444 , 7800 , ( 69 ) 7C40 , 4078 , 4040 , 7C00 , ( 70 ) 7C40 , 4078 , 4040 , 4000 , ( 71 ) 3844 , 405C , 4444 , 3800 , ( 72 ) 4444 , 447C , 4444 , 4400 , ( 73 ) 3810 , 1010 , 1010 , 3800 , ( 74 ) 3C08 , 0808 , 4848 , 3000 , ( 75 ) 4448 , 5060 , 5048 , 4400 , ( 76 ) 4040 , 4040 , 4040 , 7C00 , ( 77 ) 446C , 5454 , 4444 , 4400 , ( 78 ) 4444 , 6454 , 4C44 , 4400 , ( 79 ) 3844 , 4444 , 4444 , 3800 , ( 80 ) 7844 , 4478 , 4040 , 4000 , ( 81 ) 3844 , 4444 , 5448 , 3400 , ( 82 ) 7844 , 4478 , 5048 , 4400 , ( 83 ) 3844 , 4038 , 0444 , 3800 , ( 84 ) 7C10 , 1010 , 1010 , 1000 , ( 85 ) 4444 , 4444 , 4444 , 3800 , ( 86 ) 4444 , 4444 , 2828 , 1000 , ( 87 ) 4444 , 4454 , 5454 , 2800 , ( 88 ) 4444 , 2810 , 2844 , 4400 , ( 89 ) 4444 , 2810 , 1010 , 1000 , ( 90 ) 7C04 , 0810 , 2040 , 7C00 , ( 91 ) 3820 , 2020 , 2020 , 3800 , ( 92 ) 4040 , 2010 , 0804 , 0400 , ( 93 ) 3808 , 0808 , 0808 , 3800 , ( 94 ) 1028 , 4400 , 0000 , 0000 , ( 95 ) 0000 , 0000 , 0000 , 7C00 , ( 96 ) 1810 , 0800 , 0000 , 0000 , ( 97 ) 0000 , 3008 , 3848 , 3C00 , ( 98 ) 4040 , 7048 , 4848 , 7000 , ( 99 ) 0000 , 3840 , 4040 , 3800 , ( 100 ) 0808 , 3848 , 4848 , 3C00 , ( 101 ) 0000 , 3844 , 7C40 , 3C00 , ( 102 ) 1820 , 2070 , 2020 , 2000 , ( 103 ) 0000 , 3C44 , 3C04 , 4438 , ( 104 ) 4040 , 7048 , 4848 , 4800 , ( 105 ) 1000 , 3010 , 1010 , 3800 , ( 106 ) 1000 , 3010 , 1010 , 1060 , ( 107 ) 4040 , 4850 , 6050 , 4800 , ( 108 ) 3010 , 1010 , 1010 , 3800 , ( 109 ) 0000 , 2854 , 5454 , 5400 , ( 110 ) 0000 , 7048 , 4848 , 4800 , ( 111 ) 0000 , 3844 , 4444 , 3800 , ( 112 ) 0000 , 7048 , 4870 , 4040 , ( 113 ) 0000 , 1C24 , 241C , 0404 , ( 114 ) 0000 , 5860 , 4040 , 4000 , ( 115 ) 0000 , 3840 , 3804 , 7800 , ( 116 ) 2020 , 7820 , 2020 , 1800 , ( 117 ) 0000 , 4848 , 4848 , 3400 , ( 118 ) 0000 , 4444 , 2810 , 1000 , ( 119 ) 0000 , 4444 , 5454 , 2800 , ( 120 ) 0000 , 4428 , 1028 , 4400 , ( 121 ) 0000 , 2424 , 241C , 0438 , ( 122 ) 0000 , 7C08 , 1020 , 7C00 , ( 123 ) 0C10 , 1020 , 1010 , 0C00 , ( 124 ) 1010 , 1010 , 1010 , 1010 , ( 125 ) 6010 , 1008 , 1010 , 6000 , ( 126 ) 2454 , 4800 , 0000 , 0000 , ( 127 ) 0000 , 0000 , 0000 , 0000 , DUP \ keep a copy of start for later HERE OVER - ( addr size) \ now we have address and size HEX 800 \ this where our pattern table starts in VDP Ram SWAP ( -- addr Vaddr size ) \ arguments ready to use VMBW \ FbForth word to write bytes to VDP RAM \ VWRITE \ use this word for Camel99 Forth DP ! \ restore dictionary back to "HERE" at the top of the file You can paste this file into FbForth or Camel99 Forth and the font will be changed. In FbForth you can paste these lines into a block file and LOAD the blocks. I would add the --> word at the end of each block so that they all load with one command. In Camel99 you can paste these into the E/A editor and save as memorable file name like FONT005. Then the command INCLUDE DSK1.FONT005 will bring the font into service. FbForth Font change.mp4 fonts.zip Edited June 21 by TheBF Forgot the zip file 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5489733 Share on other sites More sharing options...
+TheBF Posted June 21 Share Posted June 21 How Do You Change Fonts Without Writing a Program? One of the very useful things about Forth is the interpreter. The interpreter gives us access to hundreds of little "programs" but we just don't always think of them that way. Here are the "programs" we used and what they do: DP DP is the dictionary pointer. It is a Forth variable so it returns the place in memory where its data is stored HERE return the contents of DP, which is the address at the end of the dictionary (ie: start of un-used memory) HEX switch the interpreter radix to use use base 16 numbers (hexadecimal) , "comma" is not a delimiter in Forth Comma is a program that takes a number from the data stack and "compiles" it into the next available memory cell in the dictionary. Then comma advances the dictionary pointer by 1 cell. (2 bytes on TI99) DUP push the top item on the data stack to the 2nd item location OVER copies the 2nd item on the data stack onto the top of the data stack. SWAP reverse the position of the top item and 2nd item of the data stack - "minus" subtract the 2nd item on the data stack from the top item on the data stack ! "store" write the data in the 2nd item on the stack into the address on the top of the data stack VMBW "VDP MULTIPLE BYTE WRITE" (called VWRITE in Camel99 Forth) given 3 parameters on the data stack ( RAMaddress VDPaddress bytes) write the specified number of bytes from CPU to VDP RAM Method: Use the dictionary as temporary storage for the data, write the data to VDP, reclaim the memory. Steps: Use HERE to remember where the data starts Use comma to copy all the patterns into dictionary memory. Use DUP to get a copy of the start address. Use HERE to get the new end of dictionary after all the data was "comma-ed" in Subtract those two addresses to get the bytes used. use the start address, the VDP address of the pattern table and the size to write the font data into VDP RAM Remember that copy of the start address? If we store that start address into the DP variable we effectively "deallocate" all that dictionary space we just used. It is now re-useable for making Forth words. To Forth, it's like it never happened. 2 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5489745 Share on other sites More sharing options...
+TheBF Posted August 27 Share Posted August 27 Not many folks are going to care about this but I am kind of jazzed about it so here it is. While learning more about implementing lists in Forth kind of like LISP I realized that I was always thinking of "lists" as data. One of the cool things about LISP is that DATA can be interpreted like CODE. Forth allows that kind of thing as well by passing strings to the word EVALUATE. S" 34 9 + ." EVALUATE However if we were to just make lists of Forth words and interpret them it would be 10X slower than compiled Forth. It occurred to me that the real answer is to make lists of "compiled" code and that way you could do anything that you could compile. But how? ANS Forth added the new word :NONAME into the language. :NONAME works like ":" but instead of associating the "execution token" (an address) of a defined word with a text name, it just gives you that magic address and you can do what you want with it. Example : HI CR ." This is a normal Forth definition" ; :NONAME CR ." This definition has "no name" so we will record the execution token" ; CONSTANT HI-TOKEN To run the word HI we just use the name and it runs itself. To run the token held in HI-TOKEN we need to pass it to the word EXECUTE like this: HI-TOKEN EXECUTE The benefit of using the tokens rather that strings is that you can still have lists of data with these token lists. How? Because every Forth word begins with a pointer to a piece of code that tells the data how to behave. I call these routines "executors" So a CONSTANT has a few instructions that say: Get the number you are holding and put it on the data stack. A VARIABLE's executor has code for: Take the address where you are keeping your contents and put it on the data stack Similarly as string literal started with S" has an executor that says put your address and string length on the data stack. In other words if we "execute" the code for these data types they can't help but do the right thing. So I wrote up a little experiment to play with the idea. This code gives us { } which counts how many things are on the data stack between the braces and returns that number TOKENS: gives us a way to take all those tokens on the data stack, compile them into memory, like an array of tokens, and give them name When you run that word with an index number it reaches into that array of tokens and PERFORMs the one at the index. (PERFORM is just like EXECUTE but it needs a memory address, it fetches the token and does EXECUTE all in one ALC word) (I took the liberty of renaming :NONAME to :: to make the code look cleaner) VARIABLE STK \ can't use CSP because it is used by the colon compiler : { ( -- ) SP@ STK ! ; : } ( -- n) STK @ SP@ - 2/ 1- ; : TOKENS: ( xt1 ... xtn n -- ) CREATE DUP , \ compile the number of items into this word 0 ?DO , LOOP \ compile all the XTs on the data stack DOES> ( ndx -- [output depends on the code] ) DUP CELL+ >R \ save copy of xt base address @ \ get the # of strings OVER U< ABORT" TOKEN: index out of range" CELLS R> + \ index into the array of XTs PERFORM \ fetch the XT and EXECUTE it ; : :: :NONAME ( -- XT ) ; \ renamed for brevity Now for example we could make lookup array for ELIZA like this: \ Since these are colon definitions, they will return the address \ of the string when TOKEN does the PERFORM function { :: S" I AM" ; :: S" I HAVE" ; :: S" I'VE" ; :: S" I'M" ; :: S" I WILL" ; :: S" I'D" ; :: S" I'LL" ; :: S" MINE" ; :: S" ARE" ; :: S" WERE" ; :: S" YOU" ; :: S" ME" ; :: S" YOUR" ; :: S" IS" ; :: S" MY" ; :: S" I" ; } TOKENS: ]PHRASES 8 ]PHRASE TYPE will show us MINE But that's not the magic part. If we wanted strings that typed themselves out when invoked we just use ." the Forth equivalent of PRINT rather than S" And... we can use any code at all in the array. { :: CR ." This 1st string will print itself" ; :: CR ." This 2nd string will also print itself" ; :: 10 0 DO CR ." And this string has a built in loop !!" LOOP ; } TOKENS: ]MAGICSTRINGS We just made a string array with embedded code. I can die happy. 2 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5522867 Share on other sites More sharing options...
+Lee Stewart Posted August 28 Share Posted August 28 21 hours ago, TheBF said: Not many folks are going to care about this but I am kind of jazzed about it so here it is. While learning more about implementing lists in Forth kind of like LISP I realized that I was always thinking of "lists" as data. One of the cool things about LISP is that DATA can be interpreted like CODE. Forth allows that kind of thing as well by passing strings to the word EVALUATE. S" 34 9 + ." EVALUATE However if we were to just make lists of Forth words and interpret them it would be 10X slower than compiled Forth. It occurred to me that the real answer is to make lists of "compiled" code and that way you could do anything that you could compile. But how? ANS Forth added the new word :NONAME into the language. :NONAME works like ":" but instead of associating the "execution token" (an address) of a defined word with a text name, it just gives you that magic address and you can do what you want with it. Example : HI CR ." This is a normal Forth definition" ; :NONAME CR ." This definition has "no name" so we will record the execution token" ; CONSTANT HI-TOKEN To run the word HI we just use the name and it runs itself. To run the token held in HI-TOKEN we need to pass it to the word EXECUTE like this: HI-TOKEN EXECUTE The benefit of using the tokens rather that strings is that you can still have lists of data with these token lists. How? Because every Forth word begins with a pointer to a piece of code that tells the data how to behave. I call these routines "executors" So a CONSTANT has a few instructions that say: Get the number you are holding and put it on the data stack. A VARIABLE's executor has code for: Take the address where you are keeping your contents and put it on the data stack Similarly as string literal started with S" has an executor that says put your address and string length on the data stack. In other words if we "execute" the code for these data types they can't help but do the right thing. So I wrote up a little experiment to play with the idea. This code gives us { } which counts how many things are on the data stack between the braces and returns that number TOKENS: gives us a way to take all those tokens on the data stack, compile them into memory, like an array of tokens, and give them name When you run that word with an index number it reaches into that array of tokens and PERFORMs the one at the index. (PERFORM is just like EXECUTE but it needs a memory address, it fetches the token and does EXECUTE all in one ALC word) (I took the liberty of renaming :NONAME to :: to make the code look cleaner) VARIABLE STK \ can't use CSP because it is used by the colon compiler : { ( -- ) SP@ STK ! ; : } ( -- n) STK @ SP@ - 2/ 1- ; : TOKENS: ( xt1 ... xtn n -- ) CREATE DUP , \ compile the number of items into this word 0 ?DO , LOOP \ compile all the XTs on the data stack DOES> ( ndx -- [output depends on the code] ) DUP CELL+ >R \ save copy of xt base address @ \ get the # of strings OVER U< ABORT" TOKEN: index out of range" CELLS R> + \ index into the array of XTs PERFORM \ fetch the XT and EXECUTE it ; : :: :NONAME ( -- XT ) ; \ renamed for brevity Now for example we could make lookup array for ELIZA like this: \ Since these are colon definitions, they will return the address \ of the string when TOKEN does the PERFORM function { :: S" I AM" ; :: S" I HAVE" ; :: S" I'VE" ; :: S" I'M" ; :: S" I WILL" ; :: S" I'D" ; :: S" I'LL" ; :: S" MINE" ; :: S" ARE" ; :: S" WERE" ; :: S" YOU" ; :: S" ME" ; :: S" YOUR" ; :: S" IS" ; :: S" MY" ; :: S" I" ; } TOKENS: ]PHRASES 8 ]PHRASE TYPE will show us MINE But that's not the magic part. If we wanted strings that typed themselves out when invoked we just use ." the Forth equivalent of PRINT rather than S" And... we can use any code at all in the array. { :: CR ." This 1st string will print itself" ; :: CR ." This 2nd string will also print itself" ; :: 10 0 DO CR ." And this string has a built in loop !!" LOOP ; } TOKENS: ]MAGICSTRINGS We just made a string array with embedded code. I can die happy. I will definitely port this to fbForth, but I will, first, need to define :NONAME and a terminator different from ; because ; toggles the smudge bit in the name-length byte of the latest-created word header in fbForth, which would not be the current, headerless word! ...lee 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5523662 Share on other sites More sharing options...
+TheBF Posted August 28 Share Posted August 28 If it helps here is how I defined :NONAME. (DISCLAIMER: I peeked inside GForth and found (:NONAME) as a common factor. HIDE and REVEAL are the Camel Forth words to smudge and and un-smudge the word. COMPILE, is just an alias for comma in simple Forths. The standards people needed something for native code systems. This is of course in my Cross-compiler Forth The X: ;X stuff is the DOS Forth's colon and sem-colon renamed, but I renamed them so I wouldn't loose my mind. : (:NONAME) ( -- ) ['] DOCOL @ COMPILE, HIDE ] ; TARGET-COMPILING X: : !CSP HEADER (:NONAME) ;X X: :NONAME HERE !CSP (:NONAME) ;X X: ; [ REVEAL COMPILE EXIT ?CSP ;X IMMEDIATE And now I see from your comment that I must be REVEALing the last word defined but I have not encountered a bug. ? I think because words are not hidden under normal circumstances. Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5523676 Share on other sites More sharing options...
GDMike Posted August 28 Author Share Posted August 28 Very good Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5523677 Share on other sites More sharing options...
+TheBF Posted August 28 Share Posted August 28 25 minutes ago, Lee Stewart said: I will definitely port this to fbForth, but I will, first, need to define :NONAME and a terminator different from ; because ; toggles the smudge bit in the name-length byte of the latest-created word header in fbForth, which would not be the current, headerless word! ...lee So it never occurred to me to deal with the semi-colon. It doesn't seem to make a problem since it is resetting the smudge bit to the default condition and the last word defined. 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5523687 Share on other sites More sharing options...
+Lee Stewart Posted August 28 Share Posted August 28 47 minutes ago, TheBF said: So it never occurred to me to deal with the semi-colon. It doesn't seem to make a problem since it is resetting the smudge bit to the default condition and the last word defined. In fbForth (inherited from figFORTH), SMUDGE toggles the smudge bit of the word whose nfa is in LATEST. After booting up fbForth 3.0, type SMUDGE and type MENU. -FIND won’t find it because you just hid it from -FIND, so using ; to terminate :NONAME will definitely screw up the last defined word with a header. Of course, if you define an even number of words with :NONAME … ; before another colon definition (which would change LATEST), there is indeed, no harm done—not so for an odd number of :NONAME … ; definitions. The word ; should definitely be redefined for :NONAME for fbForth. ...lee 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5523714 Share on other sites More sharing options...
+TheBF Posted August 28 Share Posted August 28 Well now I gotta go bug hunting. Thanks! Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5523742 Share on other sites More sharing options...
+TheBF Posted August 28 Share Posted August 28 (edited) 1 hour ago, Lee Stewart said: In fbForth (inherited from figFORTH), SMUDGE toggles the smudge bit of the word whose nfa is in LATEST. After booting up fbForth 3.0, type SMUDGE and type MENU. -FIND won’t find it because you just hid it from -FIND, so using ; to terminate :NONAME will definitely screw up the last defined word with a header. Of course, if you define an even number of words with :NONAME … ; before another colon definition (which would change LATEST), there is indeed, no harm done—not so for an odd number of :NONAME … ; definitions. The word ; should definitely be redefined for :NONAME for fbForth. ...lee I just made 3 colon definitions. Then I made a :noname definition and the colon definitions were all "findable". I take that to be because even though :noname smudged the last word defined, there is no harm because the semi-colon at the end fixes it. Am I missing something? (that's happened before) (a lot) Edited August 28 by TheBF fixed some gibberish Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5523751 Share on other sites More sharing options...
+Lee Stewart Posted August 28 Share Posted August 28 7 hours ago, TheBF said: I just made 3 colon definitions. Then I made a :noname definition and the colon definitions were all "findable". I take that to be because even though :noname smudged the last word defined, there is no harm because the semi-colon at the end fixes it. Am I missing something? (that's happened before) (a lot) That is why I qualified my comment to show only what would happen in fbForth. Your research proves that HIDE and REVEAL do not work the same as TOGGLE and SMUDGE. (TOGGLE is used by CREATE in fbForth to explicitly set both the smudge and beginning terminator bits rather than using LATEST in the process.) ...lee 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/9/#findComment-5523877 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.