+Lee Stewart Posted January 3, 2021 Share Posted January 3, 2021 3 minutes ago, TheBF said: I posted an expansion of the code in the Assembly forum since if I confused you then I was making trouble. The PUSH, macro is for any register. So in this case I did not PUSH R4 into memory stack. I only "pushed" R0 R1 & R2. I see that, now—and, I am sure you explained it to me before. I just know that I will always need to relearn that fact any time I want to write or understand written Camel99 Forth Assembler code. ...lee Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 3, 2021 Author Share Posted January 3, 2021 Yes it took me time when I bought HsForth to remember that BX was used this way. Now it's second nature. BTW how do you get Camel99 to print with the 99 sub-scripted? It looks nice. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted January 3, 2021 Share Posted January 3, 2021 1 hour ago, TheBF said: BTW how do you get Camel99 to print with the 99 sub-scripted? It looks nice. That is the font, Georgia, available on the edit menu above. ? ...lee 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 5, 2021 Author Share Posted January 5, 2021 Can you smell the burnt brain cells out there in web space? ? I have spent way to much time trying different things to marry Forth WORDLISTS to SAM memory in an elegant way. I am sure it can be done but I struggle with keeping the mental map of all the pointers to pointers to pointer-pointers... One my challenges with Forth is that I not only want to write the program, I want to write the words that let somebody else write more programs simply. Double responsibility. The context required to maintain the connections between the Forth Interpreter and compiler and arbitrary blocks of code out there in the SAMS card seem way to bulky the way I was doing it. I made something that worked fine to load up and run the code. But letting each code page be found in the wordlist search order exceeded my grasp this time. I am missing something and it doesn't seem very elegant. Next Steps: One of my specifications is I want to be able to load existing library files into SAMS without changes. I might have to make all the compiling words deferred and have a Forth version and a SAMS version and vector them all with a single command. That would let : push the sams bank# onto the return stack and ; could compile code to revert back to the previous bank# before doing EXIT. So I have lots things that work but not to my liking including this which I will call CODEX1. It works like simplified Forth79 vocabularies where each CODEX only links back to Forth. You can only compile code into the active CODEX using words in the CODEX and/or in Forth. Not overly practical but you can load up lots of utilities. You have to manually activate each CODEX by name. Not very useful yet. Spoiler \ CODEX1: SAMS Memory Forth compiling Jan 4, 2021 B Fox \ A CODEX is named 4K page in SAMS memory that contains code. \ Each code page holds its own DP and CONTEXT at the end of the SAMS page. \ Each code page is like a small vocabulary that links back to Forth. \ NEEDS DUMP FROM DSK1.TOOLS NEEDS SAMSCARD FROM DSK1.SAMSFTH HERE HEX E000 CONSTANT CSEG \ "CODE SEG" window address CSEG FFC + CONSTANT PLINKS \ local memory pointers, end of each bank 4000 CSEG 0B RSHIFT + CONSTANT CREG \ compute CSEG SAMS register VARIABLE TOTAL-AMS VARIABLE BANK# VARIABLE PAGE# CREATE HOME 0 , 0 , \ place holder for Forth DP & CONTEXT : CODEX-RESET ( -- ) EF PAGE# ! TOTAL-AMS OFF ; CODEX-RESET : NEWPAGE ( - n) PAGE# 1+! PAGE# @ DUP FF > ABORT" No more CODE pages" ; : CMAP ( bank# -- ) \ ** bank# must be in low byte** DUP BANK# ! \ last used SAMS bank SAMSCARD 0SBO \ turn on the card ( bank#) CREG ! \ store bank# in SAMS register 0SBZ ; \ turn off card : AMS-HERE ( -- addr) PLINKS @ ; : DICTIONARY ( -- dp context) DP @ CONTEXT @ @ ; : RELINK ( dp context -- ) CONTEXT @ ! DP ! ; : ACTIVATE ( bank# -- ) CMAP PLINKS 2@ RELINK ; : FAR: ( -- ) BANK# @ ACTIVATE ; : LOCAL: ( -- ) HOME 2@ RELINK ; : END-LOCAL ( -- ) DICTIONARY HOME 2! ; : BANK-MEM ( -- n ) PLINKS 2@ DROP CSEG - ; : END-SAMS ( -- ) DICTIONARY PLINKS 2! LOCAL: AMS-HERE EFFF > ABORT" SAMS page size exceeds 4K" BANK-MEM TOTAL-AMS +! ; : .SAMSCODE CR ." SAMS CODE: " TOTAL-AMS @ U. ; : CODEX: ( -- ) CREATE NEWPAGE >< DUP , CMAP CSEG CONTEXT @ @ PLINKS 2! \ init local memory pointers in SAMS DOES> @ ACTIVATE ; IMMEDIATE HERE SWAP - DECIMAL . .( bytes used) Demo code Spoiler \ CODEX1 tests CODEX-RESET \ define some named code pages CODEX: MISC CODEX: ASSEMBLER CODEX: FILES CODEX: GRAPHICS CODEX: TOOLS CODEX: SOUND \ END-LOCAL remembers where the Forth dictionary ends \ You must use it you add more code to Forth END-LOCAL MISC DECIMAL : STAR 42 EMIT ; : STARS 0 ?DO STAR LOOP ; : HI CR ." Hello world from SAMS memory!" ; INCLUDE DSK1.SOUND END-SAMS ASSEMBLER INCLUDE DSK1.ASM9900 END-SAMS GRAPHICS INCLUDE DSK1.GRAFIX INCLUDE DSK1.COLORS INCLUDE DSK1.DIRSPRIT INCLUDE DSK1.AUTOMOTION END-SAMS FILES INCLUDE DSK1.DIR INCLUDE DSK1.CATALOG INCLUDE DSK1.BLOCKS END-SAMS TOOLS INCLUDE DSK1.TOOLS INCLUDE DSK1.ELAPSE END-SAMS TEXT 17 7 VWTR DECIMAL .SAMSCODE 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 6, 2021 Author Share Posted January 6, 2021 Catching up with TI-BASIC I am reviewing everything for release V2.66 and I have never been happy with how I handled character definitions. I was also envious of BASIC's ability to handle character definition strings of arbitrary length. The secret to that feature is using a text string as the input parameter. It occurred to me that now I have all the pieces needed to make a "CALLCHAR" that works like TI-BASIC. I just had to put the pieces together. The pieces used are VDP memory manager in Forth style that lets you compile numbers into VDP memory sequentially as done in the Forth dictionary Forth's DIGIT? that converts an ascii char to a number with a conversion success flag (re-written in Assembler in V2.0) S" Forth word to define a string literal NEEDS VC, FROM DSK1.VDPMEM : >NIB ( char -- n) DIGIT? 0= ABORT" Bad char" ; : CALLCHAR ( addr len char --) BASE @ VP @ 2>R \ save radix & VDP mem pointer HEX 8* 800 + \ compute VDP address of char VP ! \ set VP pointer BOUNDS \ convert addr,len to addresses DO I C@ >NIB 4 LSHIFT \ convert left nibble & shift I 1+ C@ >NIB \ convert right nibble OR VC, \ OR them, compile into VDP RAM 2 +LOOP 2R> VP ! BASE ! \ restore VP and BASE ; EDIT: I forgot that I had to convert chars to nibbles and put them together. 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 14, 2021 Author Share Posted January 14, 2021 I was working on refining the operation of the ED99 editor. I never liked the fact that if you are copying records in SAMS memory and you only have one 4K window in memory, as I did, you have to copy the record out to RAM and then write the copy back to the new record. All this to avoid the the boundary where you want to move the last record of a page to the first record of a new page or vice versa. I recently read a presentation by Chuck Moore and he spoke about using the word BLOCK to deal with memory even when there are no disks on the system. For those who don't speak FORTH, BLOCK takes an integer argument and returns the address where you can get a "block" of disk space. This is traditionally 1K. There are typically a number of buffers and BLOCK has a enough smarts to manage the disk and memory like a virtual memory system. Wouldn't it be nice if SAMS memory could be accessed so simply? Well I took today to figure out a way. I decided to keep two 4K windows in LOW RAM. A BLOCK is 4K in size for simplicity. The code toggles back and forth between the two buffers as you request a SAMS "BLOCK". If the SAMS page is mapped into a buffer already, it just returns the buffer address. I also provide BUFF0 and BUFF1 to map in pass-thru memory for those cases where you just need regular memory for other stuff. This is not optimal code by any means but it seems to work. I will know for sure when I weave it into a new version of ED99. Spoiler \ BLOCK as a method to manage SAMS pages Jan 14 2021 Brian Fox \ NEEDS .S FROM DSK1.TOOLS HERE DECIMAL 24 USER 'R12 \ address of R12 in any Forth workspace HEX : SAMSCARD ( -- ) 1E00 'R12 ! ; \ select sams card \ using machine code so we don't need the CRU library HEX \ *set the CRU address in 'R12 before using these words* CODE 0SBO ( -- ) 1D00 , NEXT, ENDCODE CODE 0SBZ ( -- ) 1E00 , NEXT, ENDCODE CODE 1SBO ( -- ) 1D01 , NEXT, ENDCODE CODE 1SBZ ( -- ) 1E01 , NEXT, ENDCODE : SAMS-ON ( -- ) SAMSCARD 1SBO ; \ enable mapper : SAMS-OFF ( -- ) SAMSCARD 1SBZ ; \ disable mapper \ * SAMSINI sets card to "pass-through" condition : SAMSINI SAMSCARD \ select SAMS card 0SBO \ turn card on 0 \ register value stays on stack 4000 20 \ register address, # SAMS regs BOUNDS ( -- 4100 4000) DO DUP I ! \ I is reg. address I @ OVER <> ABORT" SAMSINI err" 0100 + \ next value 0101 for 1MB card, 0100 Classic99 2 +LOOP 0SBZ \ turn off card DROP ; : MAP ( block# buffer -- ) SWAP >< SWAP \ (fix this with code word) 0B RSHIFT \ divide by 2048 4000 + \ convert to SAMS register address SAMSCARD 0SBO \ enable SAMS card ! \ write block# to SAMS register 0SBZ ; \ turn off SAMS card \ ========================================== \ block manager uses handles 0 & 1 DECIMAL 256 CONSTANT HIGHBLK \ assumes 1Mb card (256*4K = 1Mb) VARIABLE USE \ BUFFER management arrays HEX CREATE BLK#S 0 , 0 , \ SAMS page in the buffer CREATE WINDOWS 2000 , 3000 , \ windows in Low CPU RAM : ]BLK# ( handle -- addr) CELLS BLK#S + ; : ]BUFFER ( handle -- buffer) CELLS WINDOWS + @ ; HEX : BUFF0 ( -- addr) 2000 02 OVER MAP ; \ map in pass-thru memory : BUFF1 ( -- addr) 3000 03 OVER MAP ; : BLOCK ( block# --- addr ) \ HIGHBLK OVER U> ABORT" SAMS page error" \ optional range test 0 ]BLK# @ OVER = IF DROP 2000 EXIT THEN \ mapped in 0? return buffer 1 ]BLK# @ OVER = IF DROP 3000 EXIT THEN \ mapped in 1? return buffer \ page is not already mapped... USE @ 1 XOR USE ! \ toggle to USE other buffer DUP USE @ ]BLK# ! \ record block# for this handle USE @ ]BUFFER TUCK MAP \ get the buffer address & map in SAMS page ; HERE SWAP - DECIMAL CR . .( bytes) 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 15, 2021 Author Share Posted January 15, 2021 SAMS BLOCK is even better in CODE I am always impressed with how Forth code maps so well onto 9900 Forth Assembler . (Gives me more ideas about a Machine Forth system that maps closer to 9900 than Chuck's ideas which were based on his F21 CPU.) Just did a timing test and it looks like this code is 9X faster than the Forth code version. \ block as SAMS manager HEX VARIABLE USE CREATE BLK#S 0 , 0 , \ SAMS page in the buffer CREATE WINDOWS 2000 , 3000 , \ windows in Low CPU RAM CODE BLOCK ( bank -- buffer) R0 BLK#S LI, \ handle 0 search R0 ** TOS CMP, EQ IF, TOS 2000 LI, NEXT, ENDIF, R0 INCT, \ handle 1 search R0 ** TOS CMP, EQ IF, TOS 3000 LI, NEXT, ENDIF, W 0001 LI, USE @@ W XOR, W USE @@ MOV, W W ADD, \ W holds offset TOS BLK#S (W) MOV, \ store the bank# WINDOWS (W) R1 MOV, \ get the window to use R1 0B SRL, \ divide by 2048 R1 4000 AI, \ convert to SAMS register address R12 1E00 LI, \ cru address of SAMS 0 SBO, \ SAMS card on TOS SWPB, \ swap bytes on bank value TOS R1 ** MOV, \ load bank into register 0 SBZ, \ SAMS card off WINDOWS (W) TOS MOV, \ return buffer on TOS NEXT, ENDCODE Edit: Did the SWPB too early in 1st version 2 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 18, 2021 Author Share Posted January 18, 2021 I am really enjoying using the BLOCK code for SAMS management. I am making some major changes to ED99 and IT really has simplified everything. Here is my new definition for a temporary buffer: : HEAP ( -- addr) 2 BLOCK ; It's so seamless. It just swaps out the SAMS page that was last used and gives me a 4K block of pass thru memory. I have to use some 32bit intermediate math to compute a record but it's in the kernel so no worries. VARIABLE BLK : ]RECORD ( n -- addr) RECSIZE UM* 4K UM/MOD 1STBLK + DUP BLK ! BLOCK + ; 1STBLK is just an offset into the 255 available blocks computed from a variable that is the file# we are editing. I record the block in use in case I might need it but so far I have not, since the editor works mostly at the line level. I built a clipboard by doing the same thing but using a "1stblock" that is above the last block used for text. DECIMAL \ clipline returns the string at top of clipstack \ clip board uses blocks 161..193 : ]CLIP ( rec# -- addr) RECSIZE UM* 4K UM/MOD 161 + BLOCK + ; Since there are two 4K buffers copying a line of text to the clipboard is just: : LINE2CLIP ( line# -- ) ]RECORD CLIPLINE COPY-REC #CLIPS 1+! ; It seems to be coming together much easier now. I hope to have new version out this week. Working on adding the ability to mark text lines (with highlighting) and cutting them out to clipboard and pasting them back in. Old version can copy one line at a time or copy the entire file. 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 21, 2021 Author Share Posted January 21, 2021 I am working hard on getting a new release of ED99. It will have some new features like hi-lighting lines for copy/delete, 1000 lines per file and paste now can insert lines anywhere in a file. I have limited it to 5 files open at once to save some room in SAMS memory for future development. It is still record based and very wasteful of the memory. Wouldn't it be nice to be able to delete files that you no longer need? Tested it here on my TI-99. (It doesn't work on Classic99 of course but will be great for working on real iron) COM1_19200bps - TI-99 DELETE.mp4 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 26, 2021 Author Share Posted January 26, 2021 Using Marker to Save Space Something I had never considered before was using the ANS word MARKER and executing a marker during compilation. Typically I use it to remove a section of a program that I am debugging so I can recompile the troublesome section and re-test. But in reviewing a library to do inverted fonts I realized I don't need the code that makes the inverted font after everything is written to VDP RAM. So why not remove the code after it has served its purpose? I took Lee's advice from some months ago to simplify this code for my next release. Since I include MARKER at startup, because I was finding it so handy, this code now takes less space. Only the words that the programmer needs remain in the dictionary. (Of course if you needed to be loading new fonts in your program you might want to keep INVERTFONT so you can always comment the MARKER and /REMOVE lines) \ HILITE.FTH create reverse color characters and removes itself \ INCLUDE DSK1.TOOLS MARKER /REMOVE \ put a "bookmark in the dictionary" NEEDS MALLOC FROM DSK1.MALLOC HEX : ]PDT ( char# -- 'pdt[n] ) 8* 800 + ; \ VDP pattern Descriptor table 400 CONSTANT 1K : INVERTFONT ( -- ) 1K MALLOC >R \ get a 1K buffer 0 ]PDT R@ 1K VREAD \ copy VDP patterns to RAM R@ 1K BOUNDS \ loop thru 1K byte by byte DO I DUP C@ INVERT SWAP C! \ invert char pattern LOOP R@ 80 ]PDT 1K VWRITE \ write RAM back to VDP upper charset R> MFREE ; \ release the memory INVERTFONT ( change the VDP memory) /REMOVE ( remove the above code from the Forth dictionary) \ *permanent routines* \ Type a string with reversed colors : HITYPE ( addr len --) BOUNDS DO I C@ 80 OR (EMIT) LOOP ; \ These operate on the VDP screen only. The original strings are not changed : HILITE ( Vaddr len --) BOUNDS DO I VC@ 80 OR I VC! LOOP ; : NORMAL ( Vaddr len --) BOUNDS DO I VC@ 7F AND I VC! LOOP ; : REVERSE ( Vaddr len --) BOUNDS DO I VC@ 80 XOR I VC! LOOP ; 3 Quote Link to comment Share on other sites More sharing options...
+9640News Posted January 26, 2021 Share Posted January 26, 2021 I don't know if any of the Forth programmers have been following some posts in the TIPI development area so want to point out an observation. I found one program, and likely it is two, McCann's The Printer's Apprentice and likely The Geometer's Apprentice that may have an issue should the user decide to print to PI.PIO if they have a TIPI. Just making a note here for others not knowing how the forth definitions are written if a user wanted to print to the PI.PIO device. The TIPI and PI require a PAB for OPEN, WRITE, and then CLOSE to properly render to PDF. I have come across Myart and The Printer's Apprentice that just WRITE without an OPEN an CLOSE. That's fine for the RS232 card, but will lock up the PI and the computer requiring a PI reboot if the OPEN and CLOSE are not implemented. Beery 1 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 27, 2021 Author Share Posted January 27, 2021 I had some communications with atrax27407 today He mentioned that Wycove Forth had a 40 column editor that used Forth blocks but mapped them onto the the TI-99 40 col. display. I had started something like that last year and abandoned it because I was not sure it was of any real use. My original used only VDP RAM and so could only hold 9K files. It saved and loaded DV80 files. I got back into it and this version uses a block file so text can be the size of the disk. The "innovation" here, if you can call it that, is the VDP RAM page is the actual buffer you edit. It seems to respond quicker because the editor never leaves VDP RAM. When you save, the VDP screen just writes the entire screen to a disk block. I created a library called "SCREENS" some time ago to let me create multiple screens in VDP RAM. I had never used it but it lets me give the editor it's own "window". I find it is pretty nice to to have 24 lines of 40 chars. Not as restricting as I thought it might be. To put information on the screen I just clear the top the line to display info and then re-read the disk block data back onto the screen. It does the trick. I am tempted to add an undo. I can keep the extra data in another VDP page I think... Spoiler ( SCREEN EDITOR in VDP buffer ) ( Jan 26 2021 Brian Fox) NEEDS DUMP FROM DSK1.TOOLS NEEDS CASE FROM DSK1.CASE NEEDS RKEY FROM DSK1.RKEY NEEDS -TRAILING FROM DSK1.TRAILING NEEDS FAM FROM DSK1.ANSFILES NEEDS MALLOC FROM DSK1.MALLOC NEEDS SCR4 FROM DSK1.SCREENS NEEDS BLOCK FROM DSK1.BLOCKS HERE \ variables ... VARIABLE INSERTING VARIABLE TOPLINE VARIABLE OFFSET VARIABLE SCR VARIABLE ^LSTK C/L@ 1- CONSTANT WIDTH \ 16 line, circular line stack \ allocated in HEAP at compile time HEX C/L@ 10 * MALLOC CONSTANT LINESTK 0F CONSTANT LMASK : LSTK++ ( ) ^LSTK DUP @ 1+ LMASK AND SWAP ! ; : LSTK-- ( ) ^LSTK DUP @ 1- LMASK AND SWAP ! ; : STKLINE ( -- addr) ^LSTK @ C/L@ * LINESTK + ; \ ================================== \ VDP screens for Forth & editor \ Vpage fg bg \ ----- -- -- 0 1 7 SCREEN: SCR0 \ forth 4 1 3 SCREEN: SCR4 \ editor : LOADSCR ( n -- ) DUP SCR ! BLOCK VPG @ 3C0 VWRITE ; : SAVESCR ( n -- ) VPG @ SWAP BLOCK 3C0 VREAD UPDATE ; HEX 1F CONSTANT ULINE 1E CONSTANT BOX : GREEN 1C 7 VWTR ; : LTGRN 13 7 VWTR ; : CYAN 17 7 VWTR ; : CLIP ROT MIN MAX ; : BETWEEN ( n lo hi -- ?) 1+ WITHIN ; : ALPHA? ( -- ?) BL [CHAR] ~ BETWEEN ; : VBLANK ( vaddr n --) BL VFILL ; : GETXY ( -- col row) VROW 2@ ; : ERASELN ( vaddr -- ) C/L@ VBLANK ; CREATE FORTHXY 0 , 0 , CREATE EDITXY 0 , 0 , EDITXY CONSTANT EROW EDITXY CELL+ CONSTANT ECOL DECIMAL : EMATCH GETXY EDITXY 2! ; : CUP VROW @ 0 > IF VROW 1-! ELSE HONK THEN EMATCH ; : CDOWN VROW @ 23 < IF VROW 1+! ELSE HONK THEN EMATCH ; : CRIGHT VCOL @ 39 < IF VCOL 1+! ELSE HONK THEN EMATCH ; : CLEFT VCOL @ 0 > IF VCOL 1-! ELSE HONK THEN EMATCH ; : NEWLINE ( ) CDOWN VCOL OFF ; DECIMAL : INS/DEL ( ) \ toggle mode INSERTING DUP @ -1 XOR SWAP ! INSERTING @ IF BOX CURS ! ELSE ULINE CURS ! THEN ; HEX \ vdp BLOCK creation 400 CONSTANT 1K : VLINE ( n -- vaddr) C/L@ * VPG @ + ; \ return vdp address of current row : ELINE ( -- VDPaddr) EROW @ VLINE ; : LNPUSH ( vaddr -- ) STKLINE C/L@ VREAD LSTK++ ; : LNPOP ( vaddr -- ) LSTK-- STKLINE SWAP C/L@ VWRITE ; : RIGHTSIDE ( -- VDPaddr len) ELINE C/L@ VCOL @ /STRING ; : LEFTSIDE ( -- VDPaddr len) ELINE VCOL @ 1+ ; \ copy vdp string to RAM address : VCOPY ( vaddr len addr --) SWAP VREAD ; \ text manipulation : DELCHAR ( -- ) RIGHTSIDE 1 /STRING TUCK PAD VCOPY BL ELINE C/L@ + VC! PAD VPOS ROT VWRITE ; : PUSHRIGHT ( -- ) RIGHTSIDE TUCK PAD VCOPY BL VPUT PAD VPOS 1+ ROT 1- VWRITE ; HEX : FORTH ( -- ) \ goto forth console GETXY EDITXY 2! SCR0 ( goto Forth VDP screen) FORTHXY 2@ AT-XY ULINE CURS ! ABORT ; HEX 400 CONSTANT 1K : TINK ( -- ) 80 SND! 2 SND! 94 SND! 25 MS 9F SND! ; : PROMPT: 0 0 AT-XY VPOS ERASELN TINK ; : .PAGE# PROMPT: ." SCR# " SCR @ . ; : PGDWN ( ) GETXY 2>R SCR 1+! .PAGE# SCR @ LOADSCR 2R> AT-XY ; : PGUP ( ) GETXY 2>R SCR @ 1- 0 MAX SCR ! .PAGE# SCR @ LOADSCR 2R> AT-XY ; : LASTLN L/SCR 1- VLINE ; : -BLANKS ( vaddr -- addr len ) 1- BEGIN 2DUP + VC@ BL = OVER 0> AND WHILE 1- REPEAT ; : EMPTY? ( vaddr -- ?) C/L@ -BLANKS NIP 0= ; : PULLUP ( ) L/SCR EROW @ DO I 1+ VLINE C/L@ PAD VCOPY PAD I VLINE C/L@ VWRITE LOOP LASTLN ERASELN ; : PUSHDN EROW @ L/SCR 1- DO I 1- VLINE C/L@ PAD VCOPY PAD I VLINE C/L@ VWRITE -1 +LOOP ; : BACKSPACE VCOL @ 0> IF CLEFT DELCHAR ELSE HONK THEN ; : LCOPY ( ) ELINE LNPUSH ; : CUT ( ) ELINE DUP LNPUSH ERASELN ; : YANK ( ) CUT PULLUP ; : DELETE ELINE EMPTY? ECOL @ 0= AND IF PULLUP THEN DELCHAR ; : PASTE ( ) LASTLN EMPTY? IF PUSHDN ELINE LNPOP ELSE HONK THEN ; : LINSRT ( ) LASTLN EMPTY? IF PUSHDN ELINE ERASELN ELSE HONK THEN ; : Y/N? ( -- ?) KEY DUP [CHAR] Y = OVER [CHAR] y = OR ; : PURGE PROMPT: ." Purge this block?" Y/N? IF SCR @ BLOCK B/BUF 20 FILL UPDATE SCR @ LOADSCR THEN ; : INFO GETXY 2>R .PAGE# 8 SPACES ACTIVE COUNT TYPE KEY DROP SCR @ LOADSCR 2R> AT-XY ; : SAVING SCR @ SAVESCR GETXY 2>R PROMPT: ." Saved block..." 300 MS SCR @ LOADSCR 2R> AT-XY ; HEX : KEYHANDLER ( char -- ) CASE 02 OF PGDWN ENDOF \ F4 03 OF DELETE ENDOF \ F1 04 OF INS/DEL ENDOF 06 OF LINSRT ENDOF \ F8 07 OF ELINE ERASELN ENDOF 0C OF PGUP ENDOF \ F6 0D OF NEWLINE ENDOF \ Enter \ cursor 08 OF CLEFT ENDOF 09 OF CRIGHT ENDOF 0A OF CDOWN ENDOF 0B OF CUP ENDOF 0E OF 0 LOADSCR ENDOF \ 90 OF PURGE ENDOF ( ^P) 93 OF SAVING ENDOF ( ^S) 95 OF VCOL OFF ENDOF 88 OF BACKSPACE ENDOF 89 OF INFO ENDOF 83 OF LCOPY ENDOF 96 OF PASTE ENDOF ( ^V) 99 OF YANK ENDOF ( ^Y) \ 9A OF UNDO ENDOF (^Z) 0F OF FLUSH FORTH ENDOF ( ESC) HONK ( unknown key) ENDCASE ; : SETUP VMODE @ 2 <> IF TEXT THEN GETXY FORTHXY 2! SCR4 PAGE LOADSCR INSERTING OFF ULINE CURS ! 0 0 2DUP EDITXY 2! AT-XY ; DECIMAL : EDIT ( scr# -- ) SETUP BEGIN RKEY DUP ALPHA? IF INSERTING @ IF PUSHRIGHT THEN VPUT UPDATE CRIGHT ELSE KEYHANDLER RKEY? DROP THEN AGAIN ; : USE BL PARSE-WORD OPEN-BLOCKS ; : REOPEN ACTIVE COUNT OPEN-BLOCKS ; HERE SWAP - . .( bytes used) VDP screen editor demo.mp4 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 28, 2021 Author Share Posted January 28, 2021 I found myself thinking about Tursi's cool BASIC program that plotted a sinusoidal wave without using bit map mode. It occurred to me that I had created a pretty efficient bit array system a while back. Could I use a bit array and map it onto a character pattern to plot in the 8x8 character matrix. Yes I could. Here is a demo program that just plots lines pixel by pixel in a character at different speeds. I should be able to plot 127 characters to plot functions with this thing. Spoiler \ PLOT to 8x8 character matrix Jan 27, 2021 Brian Fox \ BOOLEAN array creator and access words Brian Fox 2018 \ Tested with CAMEL99 Forth 16 bits, Gforth 32 bits, iForth 64 bits \ July 2019, removed Rstack juggling BSET, BRST, BTOG. \ Relaced with OVER. 20% faster NEEDS DUMP FROM DSK1.TOOLS NEEDS CHARDEF FROM DSK1.GRAFIX NEEDS ?BREAK FROM DSK1.BREAK MARKER REMOVE HEX \ calculations based on CELL size 8 CONSTANT BITS/BYTE 1 CELLS BITS/BYTE * CONSTANT BITS/CELL \ create bit array & erase memory area for 'n' bits : BITS: ( n -- ) CREATE BITS/BYTE /MOD SWAP >R \ calc memory size HERE OVER 0 FILL \ erase the memory R> CELLS + 2+ ALLOT ; \ allocate the memory \ compute bit# in a cell & cell address in memory \ usage: 42 MYARRAY BITFLD : BITFLD ( bit# bits[] -- bit#' addr) SWAP BITS/CELL /MOD CELLS ROT + ; \ convert bit# to a bit mask : BITMASK ( bit# -- n ) 0001 SWAP LSHIFT >< ; \ 9900 needs a byte swap \ API : BIT@ ( bit# bits[] -- ?) BITFLD @ SWAP RSHIFT 0001 AND ; \ return 1 bit : BSET ( bit# bits[] -- ) BITFLD SWAP BITMASK OVER @ OR SWAP ! ; : BRST ( bit# bits[] -- ) BITFLD SWAP BITMASK INVERT OVER @ AND SWAP ! ; : BTOG ( bit# bits[] -- ) BITFLD SWAP BITMASK OVER @ XOR SWAP ! ; DECIMAL 64 BITS: MATRIX HEX : ERASE 0 FILL ; : CLR ( -- ) MATRIX 8 ERASE MATRIX 80 CHARDEF ; : CALCXY ( col row -- ) 8* + MATRIX BSET ; : PLOT ( col row --) CALCXY MATRIX 80 CHARDEF ; \ Test code DECIMAL CREATE DLY 20 , : BOT2TOP CLR 8 0 DO 8 0 DO I J PLOT DLY @ MS LOOP LOOP ; : TOP2BOT CLR 0 7 DO 0 7 DO I J PLOT DLY @ MS -1 +LOOP ?BREAK -1 +LOOP ; : R2LEFT CLR 8 0 DO 8 0 DO J I PLOT DLY @ MS LOOP ?BREAK LOOP ; : L2RIGHT CLR 0 7 DO 0 7 DO J I PLOT DLY @ MS -1 +LOOP ?BREAK -1 +LOOP ; : RUN BEGIN BOT2TOP R2LEFT TOP2BOT L2RIGHT AGAIN ; HEX 80 SET# 2 3 COLOR PAGE CR CR 80 EMIT Pixel Plotting test.mp4 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 28, 2021 Author Share Posted January 28, 2021 On 1/26/2021 at 5:17 PM, BeeryMiller said: I don't know if any of the Forth programmers have been following some posts in the TIPI development area so want to point out an observation. I found one program, and likely it is two, McCann's The Printer's Apprentice and likely The Geometer's Apprentice that may have an issue should the user decide to print to PI.PIO if they have a TIPI. Just making a note here for others not knowing how the forth definitions are written if a user wanted to print to the PI.PIO device. The TIPI and PI require a PAB for OPEN, WRITE, and then CLOSE to properly render to PDF. I have come across Myart and The Printer's Apprentice that just WRITE without an OPEN an CLOSE. That's fine for the RS232 card, but will lock up the PI and the computer requiring a PI reboot if the OPEN and CLOSE are not implemented. Beery It would never occur to me to not open and close files. (Although I did write my own direct driver for RS232 so guess I shouldn't speak so quickly) Don't have TIPI yet but one day this will be useful. Thanks Beery Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 29, 2021 Author Share Posted January 29, 2021 For some reason I thought it would be tricky to make program loader for E/A 5 binaries. Turns out I had all the tools I needed and I just had to glue them together. ( addr len) below means a stack string pair. So usage is: S" DSK1.MYFILE" LOADER LOADER will load files with the incremented last character names like the E/A Cartridge Option 5. ( I have not tested that yet but I looks like it should work) \ LOADER.FTH E/A file loader for CAMEL99 Forth Brian Fox jAN 28 2021 MARKER /LOADER NEEDS LOAD-FILE FROM DSK1.LOADSAVE : LASTCHAR++ ( Caddr --) COUNT 1- + 1 SWAP C+! ; : FIELD ( n -- Vaddr) VP @ SWAP CELLS + ; : BLOAD ( addr len -- ?) VP @ 2000 13 LOAD-FILE \ VP default address is VDP>1000 3 FIELD \ -- codestart 2 FIELD V@ \ -- codestart addr ) 1 FIELD V@ \ -- codestart addr size) VREAD \ read VDP RAM to CPU RAM 0 FIELD V@ \ return the multi-file flag ; : LOADER ( addr len -- ) BEGIN 2DUP BLOAD WHILE LASTCHAR++ REPEAT 2DROP ; For anyone who might want to use this idea for FbForth or Turbo Forth beware of V@ here. In Camel99 Forth V@ reads 2 bytes (integer) like '@' does for CPU RAM. This code is not optimal but it works in Turbo Forth. HEX : VC@ V@ ; \ rename old V@ : V@ DUP VC@ 100 * OVER 1+ VC@ OR ; 1 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted January 29, 2021 Share Posted January 29, 2021 Nice 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 4, 2021 Author Share Posted February 4, 2021 I went down the rabbit hole today to see if I could squeak a little more speed out of my VDP driver. My original goal was to write most of the VDP driver in Forth. I did that but that is not optimal for speed of course however it is amazing to me how assembly language can start to bloat out your code as well. It's just a few instructions here and few instructions there but they add up. And factoring out sub-routines is less space efficient that Forth because each call is 4 bytes vs 2 in Forth. And that assumes that you don't need to nest your subroutines! So the solution that made the most difference was to create a new code word CPUT that computes the VDP address of the cursor, writes the character, compares the column to EOL and return a flag. That allowed me to keep my IF statements in Forth and to call the Forth CR word, but everything else is code. I also added a code word called (CR) which resets the column variable and increments the ROW and also returns the ROW so a Forth IF statement can do a SCROLL or not. (CR) is less important but I was in the code so there it is. It all got pretty tidy after this: : CR ( -- ) (CR) L/SCR = IF SCROLL THEN ; : (EMIT) ( char -- ) CPUT IF CR THEN ; It reduced my testing benchmark, the Sevens Problem, from 1:05.88 mins to 1:01.10, so a nice improvement, but it also added 40 bytes to the kernel which is now 8166 bytes. For the non-Forthers out there it is hard to believe but you can make programs smaller using Forth versus all Assembler when you use each for what they're best at. Sounds crazy but is true. I could not have made this fit if I had not reduced the kernel size earlier with the simplified ISO style, branching and loop code. CPUT could be made smaller if I make a sub-routine for the screen write portion of the code but that would require more overhead so I think I am done with this. CODE: CPUT ( char -- ?) \ put a char at cursor position, return eol flag TOS R2 MOV, R1 STWP, \ workspace is USER area base address 32 (R1) R3 MOV, \ vrow->r3 2E (R1) R3 MPY, \ vrow*c/l->tos 34 (R1) TOS ADD, \ add vcol VPG @@ TOS ADD, \ add video page address TOS R0 MOV, WMODE @@ BL, R2 SWPB, R2 VDPWD @@ MOV, \ write char to screen 2 LIMI, TOS CLR, 34 (R1) INC, \ bump VCOL 34 (R1) 2E (R1) CMP, \ compare VCOL = C/L EQ IF, TOS SETO, \ set true flag ENDIF, NEXT, END-CODE CODE: (CR) ( -- n) \ inc VROW , return value TOS PUSH, R1 STWP, 34 (R1) CLR, \ VCOL OFF 32 (R1) INC, \ VROW 1+! 32 (R1) TOS MOV, NEXT, END-CODE *WMODE sub-routine disables interrupts, we turn 'em back on when we finish touching the VDP. 4 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 10, 2021 Author Share Posted February 10, 2021 Camel99 2.66 Here is the newest version of Camel99 Forth This changes are really about making a kernel that supports the modern Forth idea of WORDLISTs. This means there is a "context" array that hold 8 wordlists plus a "root' wordlist. Of course we needed to add "current' variable as well to control which wordlist will get newly compiled code. A few other changes of note: New VDP driver that is between 8% to 14% faster depending on how I benchmark it WORDLISTS lib file lets you make separate name spaces either as Forth 2012 WORDLISTS or as traditional vocabularies. I needed these to create a cross-compiler on top of Camel Forth New words: ORDER show the search order ONLY resets order to just ROOT wordlist ALSO duplicates the first wordlist in the search order DEFINITIONS makes the first wordlist in the search order "current" meaning new words compile into that wordlist FORTH primary system wordlist with the system words in it. PREVIOUS return context vocabulary/wordlist to the 2nd wordlist in the search order ROOT name of the 1st wordlist in the search order MARKER, FORGET, things that touch the dictionary are not compatible with older versions. ASM9900 Forth assembler has been streamlined internally Changed the start file to put a TI logo on the screen interpretively to save space. (cute) BEGIN WHILE loops are now in ANS/ISO form so you can have multiple WHILE statements in one loop construct. Really!. It actually is handy for string functions. SAVESYS lets you save the entire system as an E/A5 program. This means you can build your own Forth with the libraries you want, save everything and have them start up immediately. I have changed the default CALLCHAR word to operate more like TI BASIC with <string>,<char> argument order. The string can also be 64 chars long. There is a SBLOCK library that uses Low RAM as buffers space for SAMS pages that swap in automagically with one command. ( n) BLOCK where n is between HEX 0 and FF and it returns the buffer address. JOYST is a short Assembler word available in a lib file Repeating key word RKEY lib has been made more stable. The Forth 2012 word SEARCH is available in a lib file. This allows string searches in large memory blocks. The FONT0230 that loads at start up now has chars for the TI Logo for 40 column and 32 col mode. It also is pre-built with inverted characters in the 8 bit charset range. See: DSK2.FONT230,SRC to see how the new font was built from source code. The lib file HILITE lets you use the inverted characters in your programs. XAM9900 is cross assembler that lets you assemble a stand alone ASM program in memory at an "ORG". It will be part of a cross-compiler in future. In the zip file: DSK1 is the CAMEL266 E/A5 program and all the library files. DSK2 is about editors, the FOXSHELL program and with the source file FSHELL,FTH and fonts DSK3 is the same. Demo files for examples of how the system is used. I appreciate all feedback since I don't actually have the time to test everything to the ground. Beat me up and I will work on making it better. What I owe you: New manual for this version Finished glossary of words. (I need an administrative assistant) Updated document of library files I have one big issue for myself: I cannot get this version to compile a version for Super Cart. ? Homemade tools, nobody to complain to... Terribly sorry. I tested this on real iron and then I must have re-built and not tested. This zip file has the CAMEL266 kernel program taken from the floppy drive right after I booted it. Note to self: Always test on real iron. CAMEL.266.ZIP 2 Quote Link to comment Share on other sites More sharing options...
atrax27407 Posted February 10, 2021 Share Posted February 10, 2021 I assume that CAMEL99 is supposed to boot as an E/A5 file by typing in: DSK1.CAMEL266 at the prompt. However, there is a problem in MAME - all I get is a blank, green screen. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 10, 2021 Author Share Posted February 10, 2021 1 hour ago, atrax27407 said: I assume that CAMEL99 is supposed to boot as an E/A5 file by typing in: DSK1.CAMEL266 at the prompt. However, there is a problem in MAME - all I get is a blank, green screen. Aha! I thought I had it running on real iron. I will do another transfer and see what's going on. Apologies. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 14, 2021 Author Share Posted February 14, 2021 On 2/9/2021 at 10:24 PM, TheBF said: Aha! I thought I had it running on real iron. I will do another transfer and see what's going on. Apologies. I believe the problem above was that I did not convert the program file to TI Files format. I really must change my cross-compiler to do provide that file format option. More work for me. Quote Link to comment Share on other sites More sharing options...
+mizapf Posted February 14, 2021 Share Posted February 14, 2021 Here are the disk images for MAME that contain the TIFILES files of the archive, as MAME cannot handle TIFILES by itself. SCTEST1 is missing on the third image because it is too short (for my TIImageTool which I used to import the files). disk1.dsk disk2.dsk disk3.dsk 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 14, 2021 Author Share Posted February 14, 2021 Thanks Mizapf. I sent @atrax27407 the system and he uses Mame. I get the sense that he fought his way through getting things on there despite my lack of help. I will have to get a copy setup myself. Vielen dank mein herr. Quote Link to comment Share on other sites More sharing options...
+mizapf Posted February 14, 2021 Share Posted February 14, 2021 Gerne geschehen. 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 15, 2021 Author Share Posted February 15, 2021 The discussion that broke out over at Erik's PEB comparing Camel99, FbForth and Turbo Forth in forced me to re-visited how I implemented my DO LOOP. I always wondered why it sucked. I was able to get the time down to 1:46.03 (about 8% faster) on the Fibonacci benchmark. Earlier when I barely knew what I was doing, I had made decisions to optimize for space because I could barely get a working kernel squashed into 8K. Lately I have found ways to save space such that I had about 80 bytes available so this was an easy trade-off. The benchmark that ran in 1:55 on V2.66: DECIMAL : FIB2 0 1 ROT 0 DO OVER + SWAP LOOP DROP ; : FIB2-BENCH 1000 0 DO I FIB2 DROP LOOP ; Reversed the logic in <LOOP> so that most of the time I don't take the code branch but fall through to Forth branch back. This removed a jump. (Seems obvious now) Used INLINE NEXT code rather than branching through a register to code in the 16 bit PAD. This surprised me. Running NEXT code inline in wait-state memory is faster. This uses 6 extra bytes for the 3 instructions but for such a commonly used structure I will keep it. CODE: <LOOP> *RP INC, \ increment loop @@2: NO IF, \ if overflow then EXIT loop *IP IP ADD, \ jump back ILNEXT, \ faster INLINE NEXT macro ENDIF, IP INCT, \ move past (LOOP)'s in-line parameter RP 4 ADDI, \ collapse rstack frame NEXT, END-CODE Using Inline NEXT on exiting the loop did not improve the benchmark time. For the curious I also have code that I tested putting the DO LOOP limit and index in registers and another version with only the index in a register and the limit remaining on the return stack. The difference in time between 2 registers and 1 registers is barely measurable so that's a non-starter. Putting the index in R15 reduced the benchmark time to 1:43.76 or about another 2% improvement so not really worth in exchange for fast multi-tasking IMHO. CODE: <LOOP> R15 INC, \ increment loop @@2: NO IF, \ if overflow then EXIT loop *IP IP ADD, \ jump back ILNEXT, \ faster INLINE NEXT macro ENDIF, IP INCT, \ move past (LOOP)'s in-line parameter R15 RPOP, \ restore old R15 RP INCT, \ remove limit from rstack NEXT, END-CODE Thanks to Lee for doing the comparisons. 2 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.