Jump to content


+AtariAge Subscriber
  • Content Count

  • Joined

  • Last visited

Everything posted by TheBF

  1. You could use the EQU to reference the string address but you need a BSS with a label somewhere with the string in it. And for a string you can also use I think. TEXT 'DSK1.1234567890'
  2. When I regained consciousness... I found that I finally had a working version of FAR: / ;FAR That reduced the overhead in the Dictionary. Using the original Turbo Forth method translated to Camel99 Forth this empty definition: FAR: TEST ;FAR Took 26 dictionary bytes and 4 SAMS bytes With the work I did this week, the same definition consumes 16 Dictionary bytes and 4 SAMS bytes. I did this by making a proper colon definition for FAR: that has it's own run-time code. This replaces all the "compiled" words in the TF code and it should be faster as well. I use two data fields: Bank# and SAMS dictionary pointer. The code word FARCOL uses the IP register to read these sequential bytes into their appropriate places. It's still not giving me as much efficiency as I would like but it reduces the Forth dictionary consumption by about 40% when I load my ANS Files library. I have tested the calling overhead yet. Bugs: 1. There is a SAMS memory bug somewhere because I cannot squeeze as many files into a 4K block as I can with more direct translation of TF version that I did earlier. ?? I think I am advancing an extra cell after each definition. More sleuthing needed... 2. Changing Banks is not reliable yet. This has been way harder than I wanted it to be but thanks to Mark paving the way I had a place to begin. Here is the current state of the code: Now if you want to load existing code need to alias FAR: ;FAR to : /; The easiest way is to use a vocabulary I think. I have not tried this but it should work. VOCABULARY SAMS ONLY FORTH ALSO SAMS DEFINITIONS \ rename normal : ; so we don't over-ride them and can still use them : H: : ; : ;H POSTPONE ; ; IMMEDIATE H: : FAR: ;H H: ; ;FAR ;H IMMEDIATE There are other ways to this that don't need vocabularies. Adding vocabulary/wordlists to my system uses 550ish valuable bytes.
  3. Don't know if this is of any value but I did something with SAMS that is pretty simple but effective. It comes from a function in Forth called BLOCK. Normally BLOCK is used for files that work as virtual memory. BLOCK takes a numerical argument from 0 to >FF It returns the CPU address of a buffer, where a block of a file is loaded, but in this case it is a 4K SAMS page that is mapped into the space. I used all of the low RAM for two 4K buffers. If the SAMS page is already in memory, nothing happens. If the page is not in memory it is pulled into the last used buffer. I only have the code in reverse polish assembler. Happy to share it but you can probably make it better yourself for your purposes. It doesn't get much simpler but it lets you grab any page anytime.
  4. It would be great to have him hanging around here again. BTW I may have discovered why Mark used a separate stack for his SAMS pages. I think you need that default page# on the bottom that he has. More experiments needed.
  5. Been there going to real iron. My condolences.
  6. Why make a new stack when you already have two? : FAR: ( -- ) : \ compile header in CPU RAM \ Run-time action POSTPONE _BANK \ compile my bank# POSTPONE DUP>R POSTPONE CMAP \ push & MAP the bank into RAM POSTPONE GOTO _BANK ]HERE @ DUP , \ compile jump to here for this bank \ compile-time action HERE TO _NHERE \ save "normal here" DP ! \ set dp to _bank's "here" _BANK CMAP \ map in the appropriate bank ; : ;FAR ( -- ) \ end banked compilation POSTPONE GOTO _NHERE , HERE _BANK ]HERE ! \ update here for bank _NHERE DP ! \ restore dp to "normal" memory POSTPONE R> POSTPONE CMAP POSTPONE ; ; IMMEDIATE I can now confirm that the code above works reliably using the return stack for SAMS pages but I think I have exhausted the possibilities of this method to put CODE into SAMS. Lee made mention of it in the Foxit thread regarding the large headers created in the Forth dictionary. I vectored the function of : and ; to be FAR: and FAR; and then compiled the following list of files into a single 4K SAMS page. INCLUDE DSK1.ANSFILES INCLUDE DSK1.CATALOG INCLUDE DSK1.DIR INCLUDE DSK1.MORE INCLUDE DSK1.GRAFIX INCLUDE DSK1.DIRSPRIT INCLUDE DSK1.COOLSPRITE Before I loaded the file I had 13,518 bytes remaining in my CPU RAM. (Forth kernel+SAMS code stuff+Tools) After loading the files here are the numbers: SAMS Page: 698 bytes free, so 3,398 bytes used CPU Free: 9514 free, so 4004 bytes used >> just for the headers<< So I will take this learning and incorporate some of it into my next work.
  7. Look another shiny object (I should be on Ritalin) I made the mistake of looking at Mark's code for compiling code into SAMS pages. It's a pretty neat hack. I would not have thought of doing it quite like this. I was able to translate it but it didn't work. Hmmm... Why? Well, TF branches to literal addresses internally. Camel Forth branches to an +/- offsets Where Mark compiles BRANCH that doesn't work in Camel Forth so I created a new Forth word: GOTO 🤣 CODE GOTO ( addr -- ) *IP IP MOV, NEXT, ENDCODE Once I discovered the magic word I started trimming it down. I have removed some of the sugar because I have less space, not using a cartridge. Colon/semi-colon is not overloaded. If you want to compile a word in SAMS use FAR: / ;FAR This is consistent with the Chuck's rule "Let the dictionary be your case statement" SETBANK is much simpler because it just tests for range and sets the _BANK value. The Bank stack has been re-written using ideas I got from Camel Forth internals. It's a little less code than using VALUEs. CMAP is narrow focused for this job and so is quite a bit smaller and faster than >MAP Todo: I think I can remove the HERE array but using the last CELL of each block to hold the dictionary pointer of the block we map in. Settle on the actual range of SAMS pages I want to use for CODE and fix the limits. Make the headers and footers in FAR: and ;FAR smaller by factoring out the existing code into a few words (some CODE would speed this up too) Use the return stack instead of _NHERE to save the Forth dictionary pointer on entry to a new word. Here is the code for CAMEL99 Forth
  8. Wonder what the converts to in metric... 🤔
  9. For reference here is MAP when you decide what it needs to do up front rather than computing stuff in the code. \ "CMAP" brings pages of code into the window called CSEG \ The SAMS register is pre-calculated as constant CREG CODE CMAP ( bank# -- ) \ ** bank# must be in low byte of R4 020C , 1E00 , \ R12 1E00 LI, 1D00 , \ 0 SBO, \ turn on the card C804 , CREG , \ TOS CREG @@ MOV, \ store bank# in SAMS register 1E00 , \ 0 SBZ, \ turn off card C136 , \ DROP \ refill top of stack register NEXT, ENDCODE
  10. With code I posted you choose the page address by setting CSEG. Your choice. Mark's code uses >3000 by default. By making the decision at compile time for the page to use, MAP becomes over 2X faster and since MAP is happening every time the word runs it's important to keep it fast.
  11. I think this can be done by storing all the extra stuff in the SAMS page that you pull in. That way you only need to remember the SAMS page# and all the data is available as soon as you MAP in the page. I did this last year but it got confusing with wordlists and vocabularies. I am looking at doing an ultra simple version because my projects are "growing to fill the know universe" I am also looking at just using the return stack for the page# value. I don't think we need a separate stack since there is a SAMS : and a SAMS ; They should save the page# and retrieve the page# when you enter and exit the words. MAP can be really simplified because we always know that the code page maps into >3000. Here it is in Forth. Computing stuff at compile time means this can be used in FbForth by changing CSEG to >E000 or where ever you need it. (SAMSCARD is just a constant for >1E00 ) : SAMSCARD ( -- ) 1E00 'R12 ! ; \ select sams card \ SAMS memory management for code HEX 3000 CONSTANT CSEG \ CPU RAM window for SAMS code 4000 CSEG 0B RSHIFT + CONSTANT CREG \ compute CSEG SAMS register CSEG 4 RSHIFT CONSTANT PASSTHRU \ default SAMS page \ "CMAP" brings pages of code into the window called CSEG \ *should be CODE for best speed : CMAP ( bank# -- ) \ ** bank# must be in low byte** SAMSCARD 0SBO \ turn on the card ( bank#) CREG ! \ store bank# in SAMS register 0SBZ ; \ turn off card I also think overloading : and ; is extra baggage that takes precious space. Using code to select the right operation at run time can be eliminated by just having a BANK selector and special colon/semi-colon pair like FAR: ;FAR The ultimate thing for me will be modifying FIND so SAMS words can call SAMS words in other pages. 🙂
  12. I just took another look at Mark's code. When I first saw it my system was not stable enough to dream about it but it all looks pretty much like I thought it should now. He uses the word SETBANK. USE I believe selects the BLOCK file. I will take a run at translating this to Camel99 Forth. I have some ideas about the bank stack implementation that could make it a bit smaller. Thanks again for the reminder about this.
  13. Sounds like you have a clear vision of where you are going and 10 or so keywords is certainly not too many to hard code them all. I have never tried TF's SAMS words but there is a bit of extra cruft needed to remember the page no. and how to return. I will have to take a look again at how Mark did that. However all that being so, we all know how very easy it is to code away and consume more resources. As Murphy's law of computing states: "All systems grow to fill the known universe" Forth can bite you because of the dictionary header and the name of the word being in the code. They say write lots of small words which helps the programmer but it eats up bytes too. Assembler on the 9900 is very efficient at inline code but nesting sub-routines can add a lot of fat that catches me by surprise in this machine Forth compiler I am trying to master. Utopia is hard to find. Keep on keepin' on with Foxit. It will be fun to see it come to life.
  14. lol. Yes indeed. I hadn't thought of that. Great catch. This thing is remarkably hard to get everything working right. As you know well, I have a unique ability to miss the details in my enthusiasm ... 🧐 @Tursi is my saviour because I can open 2 Classic99 windows. One has the compiler running in over-drive and the other opens the binary programs. Two debugger windows lets me see the Code generated and size of code generated under different conditions and the other debugger lets me step through the code. I can't imagine how much longer this would take on real iron. I have written a simple VDP driver to test the optimizer and the tail-call optimizer. It's starting to feel stable. I found a bunch of minutia that I had not considered over the last few days. I am trying to use Machine Forth rather than ALC when the code generated is the same but ALC gives nice things like accessing the 2nd item on the stack seamlessly so I use that stuff in the primitives where is simply better. Here is what seems to be a solid VDP driver. It is light-weight with no scroll or safety net and I have not done a VMBR yet. It compiles with 384 bytes. The POP/PUSH optimizer has not impact because there are no big interfaces to other words. However anything that ends with DROP will be optimized in a program that calls something that starts with DUP. I am slowly getting used to this coding style but it is not any easier however the speed it fun. Using the tail-call optimization makes you try and organize things so that words end with another word and not inline code. That's another new thing to think about. Here is a little test program used to debug the VDP driver \ hello world in machine Forth Demo Sept 23 2021 Fox \ compiles to 128 bytes COMPILER \ Use compiler wordlist (for interpreted words) NEW. HEX A000 ORIGIN. OPT-OFF INCLUDE DSK2.VDPDRVR TARGET \ -------- CREATE TXT S" Hello World! " S, PROG: MAIN HEX 8300 WORKSPACE \ Fast ram for registers 83BE RSTACK \ and return stack 83FC DSTACK \ and Data stack 17 # 7 # VWTR DECIMAL 32 # C/L ! \ init chars/line BEGIN PAGE CHAR 1 # DUP EMIT 1+ DUP EMIT 1+ EMIT 3 # 0 # AT-XY 9 # FOR TXT COUNT TYPE SPACE NEXT 13 # 13 # AT-XY TXT 1+ VPOS 5 # VWRITE AGAIN END. COMPILER SAVE DSK2.HELLO5 The video shows the program running. If you have nothing else to do here is the current Compiler. MFORTH-HELLO5.mp4
  15. Changed my mind on syntax for symbolic addressing. To stay more in the Forth syntax I am re-purposing ' (tick) when you are in the TARGET compiler mode. In machine Forth tick will just be an alias for [email protected] and return the data field address of a variable in the program image. This is the common factor. EDIT: Changed := to '! Seems more consistent. : '! ( addr1 addr2) SWAP @@ ROT @@ MOV, ; When I want to do a memory to memory move the syntax becomes: VARIABLE X VARIABLE Y ' X ' Y '! It becomes very clear why compilers use types for data so the compiler can decide how to handle things. I will avoid going there for now to see what can be accomplished this way.
  16. I am wondering... Sounds like you are going to get a considerable sized interpreter by the time you are finished with record and field creation. Wondering how you implemented the interpreter? You probably know where I am going. Should you make a command that lets you make a new command? I have seen that somewhere else... where was that?
  17. You are reminding me of a Chief engineer that I worked for many years ago. He lived through the war and managed to evade the advancing Eastern troops and made it to the western side as a 15 year old. If we complained that we needed better tools he would say "Anybody can fix it with tools! After the war all I had was toe nail clippers and my grandmother's nail file"
  18. I got my information from this thread.
  19. How to choose the best addressing mode with a very "dumb" compiler I have been struggling with keeping the Machine Forth as "normal" as possible because my old brain needs that. So as mentioned I decided that data like variables and constants simple pushed themselves onto the data stack. This keeps it normal but consumes three instructions for each argument. ( two for the push and one for LI instruction) 9900 has that wonderful symbolic mode where you can move things memory to memory. So how best to write that when compiler is too stupid to figure it out? Constants are a not issue because at the moment they only generate code when you use them in the program. Variables are a real memory address in your program with the data held there. I made this word to retrieve the DATA field of a variable (the address) : [email protected] ( <NAME> -- addr) ' >BODY @ REL>TARG ; It looks up the variable, fetches the contents and converts the address from relative to the real target address in the program image. Then we can create : LET [email protected] ; \ get address of a variable : := [email protected] ( addr1 addr2) SWAP @@ ROT @@ MOV, ; Using these words we can create: VARIABLE X VARIABLE Y LET X := Y \ :-))) LOL. This compiles to: MOV @X,@Y Not very Forthy but it's the best I can come up with. :)
  20. Tail Call Optimization Oversight I have been working on making some solid VDP library routines in this weird language and when I started applying my fancy new tail call optimization I discovered something I completely missed. Example Sub-routine with tail-call optimized : PAGE ( -- ) 0 # 768 # 32 # VFILL -; This code clears the screen in 32 column mode. It is just a wrapper for VFILL and three parameters to fill VDP RAM at address 0 with 768, ASCII 32 characters. Notice we end with -; and not the normal ; This is how you end a sub-routine when you know the last word is another sub-routine and so you can eliminate the "return from sub-routine" code. -; looks back in the code and removes the normal BL @VFILL and replaces it with B @VFILL. Still blows my mind that this works. BUT! every sub-routine in this system has a preamble of two instructions to save R11 on the return stack like this: DECT R7 MOV R11,*R7 Since we are not "calling VFILL" but using Branch we don't need to make room on the stack and save R11. This means we don't want to enter at the normal entry address but 2 CELLS after. (CELL is Forth speak for an integer memory location) So the definition for -; has been changed to the code below and it seems to work perfectly now. (at least until I find the next bug) \ tail call optimizing semi-colon H: -; ( -- ) LOOKBACK ( addr ) \ get entry address of sub-routine 2 CELLS + ( addr' ) \ move past sub-routine RPUSH instructions -04 TALLOT \ erase BL @addr ( addr') @@ B, \ compile a branch to the NEW sub-routine ;H Edit: The next thing I should do is look back before the sub-routine address and make sure there is BL instruction. This could error out if not or I could make tail-call removal automatic.
  21. Found on Twitter. "Whenever someone asks for unit testing I'm sending them this" 😂
  22. Thank you. I will implement your changes.
  23. FOXSHELL built on Latest Sources Doing a some maintenance and I have a new version of the FoxShell. I have corrected some oversights in the Kernel so they are in here now. The ANS file support is smaller now so that is reflected. Also I have not included DSK1.TOOLS in this version. Since this is just a Forth system under the hood you can INCLUDE DSK1.TOOLS from the Camel99 Forth DSK1 if you need it. This shell is actually a nice system to use for program development since you have all the disk utilities at your finger tips and it reboots immediately. The COLD command now resets the FoxShell to the default dictionary so can load Forth code and play around and just type COLD to remove it and get a clean system. Please report bugs and/or any features you wish it had. EDIT: Made Lee's changes to the source code. Zip file is also new. Changed MORE so you can see the contents of a DF128 file. (Not fully tested) Usage: DF128 MORE DSK2.MY128FILE COLD resets the file access mode to DV80. Source: FOXSHELL1.4.ZIP
  24. And more proof that it's best to remove code from the inner loop. Just changing OVER + SWAP to the optimized word BOUNDS dropped the time to 22 seconds. DECIMAL : FIB 0 # 1 # ROT 0 # DO \ OVER + SWAP BOUNDS LOOP DROP ; PROG: MAIN 1000 # 0 # DO I FIB DROP LOOP NEXT, \ Return to Forth END. The difference in the code inside the loop is: OVER + SWAP TOS DPUSH, 2 (SP) TOS MOV, *SP+ TOS ADD, TOS R0 MOV, *SP TOS MOV, R0 *SP MOV, BOUNDS *SP R1 MOV, TOS *SP ADD, R1 TOS MOV, Chuck's machine Forth does not have SWAP.
  • Create New...