+TheBF Posted August 4 Author Share Posted August 4 (edited) OK so this is preliminary but from what I can see this new version which uses both of @Tursi 's recommendations is really fast. 1. Keep the virtual-to-real address convertor out of the Assembler code loops. 2. Use a 16bit fill operation But the implementation is a bit different. The system has FILL built in so we use that for potential odd number writes at beginning and end We use Forth to chunk through the addresses and serve them to the assembler routines. The way this code works now it outputs the first chunk, ie: from the start address to the first page boundary. Then if there is more than another >1000 bytes it enters the loop and uses FILLW for the entire loop which is 2x faster. And the last line takes whatever is left over and uses FILL again. HEX 1000 CONSTANT 4K \ compute start of next SAMS page boundary ( 1000,2000,3000 ETC) : BOUNDARY ( addr -- addr') F000 AND 4K + ; : CHUNK ( addr len -- addr len addr1 n1) OVER DUP BOUNDARY OVER - 1FFF AND ; : NEXTCHUNK ( addr len -- addr len addr len') CHUNK DUP>R \ compute chunk and save length 2SWAP R> /STRING 2SWAP \ reduce size of data by length ; \ fast 16bit character fill CODE FILLW ( adr len char --) TOS R1 MOV, \ dup char in R1 R1 SWPB, \ shift to other byte R1 TOS ADD, \ make char in both bytes of TOS *SP+ R0 MOV, *SP+ W MOV, BEGIN, TOS *W+ MOV, \ 2 chars are in TOS register R0 DECT, \ decr. count by two NC UNTIL, \ loop until carry flag reset TOS POP, NEXT, ENDCODE HEX : SAMS.FILL ( addr len char --) >R NEXTCHUNK SWAP >REAL SWAP R@ FILL \ handle 1st chunk BEGIN DUP FFF U> WHILE NEXTCHUNK \ LENGTH always = >1000 SWAP >REAL SWAP R@ FILLW REPEAT ABS SWAP >REAL SWAP R> FILL ; Here is the timing result for 64K bytes filled in SAMS memory. Edited August 4 by TheBF Wrong comment 5 Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 5 Share Posted August 5 Nice work! 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 5 Author Share Posted August 5 16 minutes ago, Tursi said: Nice work! Good coaching helps! Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 5 Author Share Posted August 5 (edited) Green software... While working with this thing a bit more and trying to extend what I learned to MOVE, I discovered that I needed to detect the condition when a fill does not cross a page boundary. So another definition call CROSSING? and some re-naming to save bytes and the final SAMS.FILL looks like this: I am discovering that testing the possible variations for ( addr addr len ) MOVE is going to be a pain unless I get a magic insight. HEX 1000 CONSTANT 4K \ compute start of next SAMS page boundary ( 1000,2000,3000 ETC) : BOUNDARY ( addr -- addr addr') F000 AND 4K + ; \ test (address,len) crosses a page boundary : CROSSING? ( addr len -- addr len ?) OVER BOUNDARY -ROT + U< ; : CHUNK ( addr len -- addr len addr1 n1) OVER DUP BOUNDARY OVER - 1FFF AND ; : NEXTCHUNK ( addr len -- addr len addr len') CHUNK DUP>R \ compute chunk and save length 2SWAP R> /STRING 2SWAP \ reduce size of data by length ; \ fast 16bit character fill CODE FILLW ( adr len char --) TOS R1 MOV, \ dup char in R1 R1 SWPB, \ shift to other byte R1 TOS ADD, \ make char in both bytes of TOS *SP+ R0 MOV, *SP+ W MOV, BEGIN, TOS *W+ MOV, \ 2 chars are in TOS register R0 DECT, \ decr. count by two NC UNTIL, \ loop until carry flag reset TOS POP, NEXT, ENDCODE : SAMS.MEM ( addr len -- sams len) SWAP >REAL SWAP ; HEX : SAMS.FILL ( Virtual len char --) >R \ No page crossing fill handler 2DUP CROSSING? 0= IF SAMS.MEM R> FILL EXIT THEN \ Page crossing handler NEXTCHUNK SAMS.MEM R@ FILL \ handle 1st chunk BEGIN DUP FFF U> WHILE NEXTCHUNK \ LENGTH always = >1000 SAMS.MEM R@ FILLW REPEAT ABS SAMS.MEM R> FILL ; Edited August 6 by TheBF Updated code 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 6 Author Share Posted August 6 Oh man. I need to get better at testing. CROSSING? had '<' operator. I needs to be U< to handle 64K of memory !! Fixed the previous post. 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 9 Author Share Posted August 9 Conclusion after this effort is that SAMS is still pain to use. After getting stomped by all the corner cases I eventually ended up with this final code, which works but seems way to complicated for a FILL routine. It was a good learning process but when I looked at doing the same to CMOVE it got pretty crazy. It might be easier using variables or values but It would still involve a lot of incremental moves since the source and destination could be anything. "Doctor it hurts when I do this" "Well then don't do that!" I am going to regroup. I have a clean interface with cell and byte access for fetch and store and a clean way to convert a virtual address to a real address. I think I need to use SAMS in chunks of 1K (like BLOCK) or power of 2 byte sized chunks. Doing that makes it simple to avoid page crossing moves and fills. And I think it makes more sense to do the virtual address to real address conversions where you need them using >REAL, rather than tons of stack gyrations to make is look prettier. More to come... Spoiler \ SAMS.FILL experiment HEX 1000 CONSTANT 4K : SAMS.MEM ( addr len -- sams len) SWAP >REAL SWAP ; : BOUNDARY ( addr -- addr addr') F000 AND 4K + ; : CROSSING? ( addr len -- addr len ?) OVER BOUNDARY -ROT + U< ; : CHUNK1 ( a u -- a u a' u') OVER DUP BOUNDARY OVER - ; : /CHUNK ( a u a1 u1 -- a' u' a1 u1) DUP>R 2SWAP R> /STRING 2SWAP ; : 1STCHUNK ( a1 len -- addr len addr1 n1) CHUNK1 /CHUNK ; \ used ONLY on even page boundaries : NEXTCHUNK ( addr len -- addr' len' addr len') 2DUP BOUNDARY 1000 UMIN /CHUNK ; \ debug tool \ : .OUTPUT ." //" SWAP . . CR ; \ : SNIFF 2DUP .OUTPUT ; \ fast 16bit fill HEX CODE FILLW ( adr len char --) TOS R1 MOV, \ dup char in R1 R1 SWPB, \ shift to other byte R1 TOS ADD, \ char in both bytes of TOS *SP+ R0 MOV, *SP+ W MOV, BEGIN, R0 DECT, \ decr. count by two OC WHILE, TOS *W+ MOV, \ 2 chars are in TOS register REPEAT, \ loop until carry flag reset TOS POP, NEXT, ENDCODE \ a little protection : ?64K ( addr len -- ?) 0 SWAP 0 D+ NIP ABORT" SAMS: >64K" ; HEX : SAMS.FILL ( Virtual len char --) >R 2DUP ?64K OVER EFFF U> >R \ last page? 2DUP CROSSING? 0= \ NOT crossing pages R> OR \ either condition true IF SAMS.MEM R> FILL EXIT \ simple fill and get out THEN \ multi-page fill 1STCHUNK SAMS.MEM R@ FILL BEGIN DUP FFF U> WHILE NEXTCHUNK SAMS.MEM R@ FILLW REPEAT SAMS.MEM R> FILL \ leftover bytes ; : SAMS.ERASE 0 SAMS.FILL ; 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 14 Author Share Posted August 14 OK. I have the beginning of something useful for holding compact string data in SAMS pages and loading them from a file. I played with a number of options and the best I could come up with was: Always use "virtual addresses" ( >0000..>FFFF) and only convert to "real" addresses (ie: map in a page) when you want to read or write something into SAMS memory Place counted strings in SAMS starting at virtual address 0 and load strings until there is not room in the 4K page, for a full text record of 80 bytes + the count When the SAMS page is "full" per #2, advance to the next page boundary. This is quite efficient using AND and + It means that as you are loading strings you must test for the boundary condition, but it seems pretty quick. To read the string from SAMS we do the same as with normal RAM and use the count-byte of the string to compute the jump to the next string. The only different is now when we advance, we check for a boundary and if the page is full per the previous method, we jump to the next SAMS page. All of this is in the pursuit of making it possible to edit larger files in VI99 or other editors without wasting tons of memory by using 80 byte records in SAMS. The hard part will be doing insertion and deletions into this mess but it should be possible. The video has a 284 line file being loaded and partially printed. Spoiler \ HEAPSTR-SAMS.FTH loads dv80 into SAMS segment Aug 13 2024 Brian Fox \ Very compact string storage \ Loads file data as counted strings in a SAMS segment NEEDS DUMP FROM DSK1.TOOLS NEEDS READ-LINE FROM DSK1.ANSFILES NEEDS >REAL FROM DSK1.SAMSPROG \ HERE \ ===[ virtual SAMS.HEAHEREnagement ]==== HEX 1000 CONSTANT 4K 8000 CONSTANT SAMS.SIZE DECIMAL \ select the segment and return the start of virtual memory : TXTBUFFER ( -- ) 1 SEGMENT ; : ?TXTMEM ( n --) SAMS.SIZE 0 WITHIN ABORT" SAMS.ALLOT err" ; \ sams dictionary pointer holds a "virtual address" in a SAMS segment VARIABLE SDP : SAMS.HERE SDP @ ; : SAMS.ALLOT ( n --) SDP 2DUP @ + ?TXTMEM +! ; : SAMS, ( n --) SAMS.HERE !L 2 SAMS.ALLOT ; : SAMSC, ( c --) SAMS.HERE C!L 1 SAMS.ALLOT ; : SAMS.ALIGN ( -- ) SAMS.HERE ALIGNED SDP ! ; \ fill 4k pages at a time. *NO PROTECTION* : SAMS.FILL ( addr len char --) >R BOUNDS BEGIN 2DUP <> WHILE DUP >REAL 4K R@ FILL 4K + REPEAT R> DROP 2DROP ; \ purge erases all pages, resets line counter HEX : PURGE ( -- ) SDP OFF 0 SAMS.SIZE 0 SAMS.FILL LINES OFF 0 SAMSC, ; \ 0th string is null HEX \ compute the address of the next page boundary after address : BOUNDARY ( addr -- addr') F000 AND 4K + ; \ test if page can hold one more 80 char line + count byte (aligned) : PAGEFULL? ( Virtaddr -- ?) 0FFF AND [ 4K 52 - ] LITERAL > ; \ forces SAMS memory pointer to even 4K boundary if full : ?BOUNDARY SAMS.HERE PAGEFULL? IF SAMS.HERE BOUNDARY SDP ! THEN ; : SAMS.PLACE ( src n sams -- ) >REAL PLACE ; \ compile RAM string as a counted string into SAMS segment : SAMS.S, ( c-addr u -- ) 1 MAX ?BOUNDARY SAMS.HERE OVER 1+ SAMS.ALLOT SAMS.PLACE ; DECIMAL \ copy VDP string into SAMS segment as a counted string : SAMS.VPLACE ( VDP n sams -- ) >REAL 2DUP C! 1+ SWAP VREAD ; \ compile VDP string as counted string into SAMS segment : SAMS.VS, ( Vaddr u -- ) 1 MAX ?BOUNDARY SAMS.HERE OVER 1+ SAMS.ALLOT SAMS.VPLACE ; \ return the VDP address and length of the record in the PAB : FDATA ( -- Vaddr len) [PAB FBUFF] V@ [PAB CHARS] VC@ ; \ ---------------------------- \ user API ... \ usage: S" DSK1.MYFILE" READ-FILE : READ-FILE ( addr len -- ) DV80 R/O OPEN-FILE ?FILERR >R \ file handle to rstack PURGE LINES OFF BEGIN 2 FILEOP 0= \ faster than ANS read-line WHILE FDATA SAMS.VS, LINES 1+! REPEAT R> CLOSE-FILE ?FILERR ; \ advance virtual address by the length of the string +1 (for count byte) : >NEXT ( Virtaddr -- Virtaddr') DUP C@L + 1+ ; : SAMS.NEXT$ ( Virtaddr -- Virtaddr') >NEXT DUP PAGEFULL? IF BOUNDARY THEN ; DECIMAL \ seek Nth string in SAMS buffer using string lengths to jump : NTH ( virtaddr n -- virtaddr') 0 ?DO SAMS.NEXT$ LOOP ; \ ------------ \ debugging : .SAMS$ ( VIRT -- Virt ) DUP >REAL COUNT TYPE ; : LISTALL ( -- ) 0 ( start of buffer ) BEGIN CR .SAMS$ SAMS.NEXT$ ?TERMINAL ABORT" halt" DUP C@L WHILE REPEAT ; \ usage: S" DSK1.MYFILE" WRITE-FILE : WRITE-FILE ( addr len -- ) DV80 R/W OPEN-FILE ?FILERR >R 1 \ text starts at byte 1 BEGIN DUP C@L WHILE DUP >REAL COUNT \ 2DUP CR TYPE R@ WRITE-LINE ?FILERR SAMS.NEXT$ REPEAT DROP R> CLOSE-FILE DROP ; Classic99 QI399.046 2024-08-14 16-55-47.mp4 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 15 Author Share Posted August 15 (edited) Newest Kernel and SAMSPROG files. In case anybody actually plays with this system besides me, here is what I am running these days in both the high-ram version and the super-cart version. Put the TI-99 files on your CAMEL99 system disk and mount in DSK1. CAMEL99 is the high RAM kernel CAML99SC is the super cart kernel that gives you and extra EIGHT THOUSAND BYTES of extra dictionary space SAMSINI is common code to all my SAMS experiments now. SAMSPROG needs it SAMPROG is a SAMS virtual memory system the uses a separate workspace. Registers are used instead of variables START is loaded and compiled when the kernel COLD boots. (or you type CODE) It loads a font file. If you don't want comment out those lines. SYSTEM adds some Forth words to make the KERNEL have the CORE standard Forth dictionary (Don't edit that without making a copy) SAMSPROG lets you do the nifty conversion from a virtual address in a 64K "segment" of SAMS memory, to a "real" address in low RAM with the word >REAL. It also lets you access memory in the active SAMS segment with "long" versions of fetch and store called @L !L C@L C!L These words are all sub-programs called by BLWP. ALL of low RAM is used for two 4K "windows" and the nifty code will always put the SAMS page into the opposite window of the one you last used. This way you can copy and move (with caveats) from one place in SAMS to another place in SAMS without copying into another buffer. The current SAMSPROG loads the ASM9900 assembler, TOOLS, ELAPSE, the stuff I used for development. I need to make a machine code version but I want to make SAMPROG do something useful first to find bugs. Edit removed kernel programs with non-compliant 2>R 2R> SAMSPROG SAMSINI START SYSTEM Edited August 19 by TheBF 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 19 Author Share Posted August 19 (edited) Mea culpa. I am trying to make something work with linked lists. (more to come) I found a little package at forthstandard.org that looked interesting. It uses 2>R and then picks off an item with R>. My 2>R put things on the return stack upside down. Oopsie. To that one very special person who downloaded the new kernels, please replace your kernels with these two versions. CAMEL99 CAML99SC Edited August 19 by TheBF 1 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 19 Author Share Posted August 19 Comparing 9900 code to Intel. In the course of fixing 2>R and 2R> I took a look at how GForth did it. Oh my goodness. I didn't even try to understand it but look at how many instructions were used. SEE 2>R Code 2>r ( $403188 ) mov dword ptr 41F2D8 , ebp ( $40318E ) mov edx , dword ptr 41F2E8 ( $403194 ) mov esi , dword ptr 4 [edi] ( $403197 ) mov dword ptr AC [esp] , esi ( $40319E ) mov ecx , dword ptr [edi] ( $4031A0 ) lea eax , dword ptr F8 [edx] ( $4031A3 ) mov 41F2E8 , eax ( $4031A8 ) mov dword ptr A8 [esp] , ecx ( $4031AF ) mov dword ptr FC [edx] , esi ( $4031B2 ) add edi , # 8 ( $4031B5 ) mov eax , 41F2E8 ( $4031BA ) mov dword ptr [eax] , ecx ( $4031BC ) mov eax , dword ptr 0 [ebp] ( $4031BF ) add ebp , # 4 ( $4031C2 ) mov esi , eax \ $89 $C6 ( $4031C4 ) mov ebx , dword ptr [eax] ( $4031C6 ) jmp 40152A end-code SEE 2R> Code 2r> ( $4031CB ) mov dword ptr 41F2D8 , ebp ( $4031D1 ) mov eax , 41F2E8 ( $4031D6 ) sub edi , # 8 ( $4031D9 ) mov edx , dword ptr 4 [eax] ( $4031DC ) mov dword ptr A4 [esp] , edx ( $4031E3 ) mov edx , dword ptr [eax] ( $4031E5 ) mov ecx , dword ptr A4 [esp] ( $4031EC ) add eax , # 8 ( $4031EF ) mov dword ptr A0 [esp] , edx ( $4031F6 ) mov 41F2E8 , eax ( $4031FB ) mov dword ptr 4 [edi] , ecx ( $4031FE ) mov dword ptr [edi] , edx ( $403200 ) mov eax , dword ptr 0 [ebp] ( $403203 ) add ebp , # 4 ( $403206 ) mov esi , eax ( $403208 ) mov ebx , dword ptr [eax] ( $40320A ) jmp 40152A end-code Here are my versions (correct now) in lovely 9900 Forth Assembler CODE 2>R ( d -- ) ( r-- n n) RP -4 ADDI, TOS *RP MOV, *SP+ 2 (RP) MOV, TOS POP, NEXT, ENDCODE CODE 2R> ( -- d ) TOS PUSH, SP DECT, TOS RPOP, *SP RPOP, NEXT, ENDCODE 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 20 Author Share Posted August 20 So I have gone down the Rabbit Hole on learning about the old VI editor. I read that it uses a swapfile so that you can edit a file larger than memory. I tried making an editor like that using a block file. It worked but was not practical when you tried to delete and insert lines. And even the way I made VI99 will bog down if have 7K+ of text. So... I am experimenting using a linked list that holds a record number in a 64K block file. The goal is to make something that works even on a Floppy drive based TI99. I found my old files that make a LISP style pool of link cells and I had to relearn how they worked. This is from a "Mini-LISP" written by Martin Tracy in 1985, but I need an entire LISP so I had to "Forthify" it. Using a few years more experience I made it work a bit better. The concept for the editor is the file records will live on disk as counted strings with 4 - 1K RAM buffers. 32K of file is for editing and 32K starting from the other end is HEAP of records for insertions. The link pool will support 800 links. The records are connected via the linked list. When you insert a line you get a line from the disk file, get it a link and then insert that link into the list. Deletions will mark the link free and set the record count on disk to zero. The problem I have is I don't have a method yet for reclaiming links from the pool that I don't need. I have never created a garbage collector before... so that could take awhile. The LISP approach may be to heavy and I could make simpler linked list system but the LISP system kind of amazing. Here is the working lisp code. It takes some grokking to figure this thing out. \ static lists in Forth Updated: Mar 10 2022 \ based on code by Martin J Tracy Nov 17 1985 NEEDS DUMP FROM DSK1.TOOLS NEEDS MARKER FROM DSK1.MARKER NEEDS MALLOC FROM DSK1.MALLOC MARKER /LISTS \ utility words : ERASE ( addr len -- ) 0 FILL ; : CELLS+ ( n -- n') CELLS + ; : NOT ( n -- ? ) 0= ; : .$ ( addr -- ) COUNT TYPE ; \ lists are two cell structure with a pointer to the next item \ and a pointer to the data \ \ [link],[^data] \ For historical reasons the link is called the CDR \ The data pointer is called the CAR \ There we can create an "NIL" list in Forth like this \ cdr car \ ----- ----- CREATE NIL NIL , NIL , \ extracts the value pointer of the first link of a list : CAR ( list -- ^value) CELL+ @ ; \ extracts the next link of a list (follows the list) : CDR ( list -- list') @ ; : CADR ( list -- next-item) CDR CAR ; \ returns true if list is NIL (empty) : NULL ( list -- f) NIL = ; \ Allocate a Pool of links DECIMAL 1000 CONSTANT #LINKS \ 1000 links for now. 2 CELLS CONSTANT CONSIZE \ size of 1 CONS structure #LINKS CONSIZE * CONSTANT POOLSIZE \ CONS cells are allocated in the HEAP as "pool" POOLSIZE MALLOC CONSTANT POOL VARIABLE FREE-CONS \ points to an available link \ Create pool of CONS cells as one list of "FREE-CONSs" : NEW-LINKS ( --) POOL POOLSIZE ERASE POOL #LINKS 1- 0 \ all links but last DO DUP CONSIZE + OVER ! \ make cons cell CONSIZE + \ bump address to next cell LOOP NIL SWAP ! \ last link is NIL POOL FREE-CONS ! ; \ set FREE-CONS to start of CONS memory NEW-LINKS \ init the POOL array now : ?MEMFULL ( addr --) NIL = ABORT" () No free links" ; : NEW-CELL ( -- cell) \ allocate a free cell and return its address FREE-CONS @ DUP ?MEMFULL DUP CDR FREE-CONS ! ; \ CONS is the fundamental LIST contructor \ add a link whose CAR (1st list item) points to the value : CONS ( ^value list -- list') NEW-CELL DUP>R 2! R> ; \ ================================================================= \ list validation. (too complicated?) DECIMAL : UWITHIN ( address low-bound high-bound -- ?) >R OVER R> U< -ROT U< NOT AND ; : INHEAP? ( addr -- ?) POOL [ POOL POOLSIZE + ] LITERAL UWITHIN ; : ALIST? ( addr -- ?) \ true if the address argument is a list or NIL DUP INHEAP? IF CDR 0 <> ELSE NULL THEN ; : ?LIST ( list -- list) DUP ALIST? NOT ABORT" Not a list" ; : ?NIL ( list -- list) ?LIST DUP NIL = IF ." (NIL)" CR ABORT THEN ; \ =======================[ list operations ] ======================= : NTH ( list n -- list) 0 ?DO CDR LOOP ; : LENGTH ( list -- n) \ finds the number of items in a list. 0 ( accumulator) BEGIN OVER NIL <> WHILE 1+ SWAP CDR SWAP REPEAT NIP ; \ perform XT on every item in the list : MAP ( list2 xt) >R ?LIST BEGIN DUP NIL <> WHILE DUP CAR R@ EXECUTE CDR REPEAT R> 2DROP ; : '(' ." ( " ; : ')' ." )"; : .LIST ( list --) '(' ['] . MAP ')' ; : U.LIST ( list -- ) '(' ['] U. MAP ')' ; : REVERSE ( list -- new-list) ?NIL NIL SWAP BEGIN DUP NIL <> WHILE DUP CAR ROT CONS ( create a new list with CONS) SWAP CDR REPEAT DROP ; : LAST ( list -- list') \ returns last link of given list as a list. ?NIL BEGIN DUP CDR NIL <> WHILE CDR REPEAT ; : APPEND ( list1 list2 -- list1+list2) \ appends two lists. The originals are "surgically" joined OVER ?LIST OVER ?LIST DUP NIL <> IF OVER NIL = IF NIP ELSE OVER LAST ! NIP NIP THEN ELSE DROP THEN ; : LERASE ( list -- ) \ erases, but does not return links to pool... YET BEGIN DUP CDR NIL <> WHILE DUP CAR OFF CDR REPEAT CAR OFF ; \ LIST-VARS are Forth variables that hold a list \ Access them with @ and ! : LIST-VAR CREATE NIL , ; \ add an item to the start of a list : !! ( item listvar -- ) DUP>R @ CONS R> ! ; : #ITEMS ( - n) CSP @ SP@ - 2/ 1- ; : { ( -- ) !CSP ; : } ( -- nil n) #ITEMS NIL SWAP 0 ?DO CONS LOOP ; And here is how it can be used \ Manually Construct a list with CONS and a list "variable" LIST-VAR LIST1 1 2 3 NIL CONS CONS CONS LIST1 ! \ make a list using curly brackets LIST-VAR LIST2 { 4 5 6 7 8 9 } LIST2 ! 0 LIST1 !! LIST1 @ .LIST LIST1 @ LAST .LIST LIST2 @ .LIST LIST2 @ REVERSE .LIST LIST1 @ LIST2 @ APPEND .LIST Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 25 Author Share Posted August 25 Something I always wanted for myself was a command to go back to the E/A menu rather than to the title screen. Since I have yet to make an text-file editor that I am totally happy with, this makes it much easier to use the E/A editor or Fred's improved editor as you choose. So here are couple of "scripts" than make a more complete version of Forth; more of a development version we could say. We throw in the Assembler + labels, the TOOLS and add DIR CAT and MORE (to see text file content). And we change the BYE command so it returns to the E/A menu. For that one lonely person out there who tries this system, maybe this will make life a little less painful. Here are two scripts. One that builds on the regular kernel and the 2nd builds on the Supercart Kernel. I save the binary files off to DSK7 on my Classic99 setup but you can change that to where you want it to go. I have also attached the working binary files so you can use them right away. Spoiler \ DEVSYS.fth Builds a big system on Camel99 Aug 2024 Brian Fox CR .( compiling development tools ) \ Load what you need here INCLUDE DSK1.MARKER INCLUDE DSK1.WORDLISTS INCLUDE DSK1.ANSFILES HEX : GRAPHICS ( -- ) 1 VMODE ! 0 3C0 0 VFILL 00 2 VWTR E0 DUP 83D4 C! 1 VWTR 0E 3 VWTR 01 4 VWTR 06 5 VWTR 01 6 VWTR 0 380 10 10 VFILL 8 7 VWTR 20 C/L! PAGE ; \ 83E0 LWPI, 0070 @@ B, CODE MENU 02E0 , 83E0 , 0460 , 006A , ENDCODE \ BYE will now return to E/A menu, TI-99 GPL System : BYE GRAPHICS MENU ; DECIMAL VOCABULARY TOOLS VOCABULARY ASSEMBLER ONLY FORTH ALSO TOOLS DEFINITIONS INCLUDE DSK1.TOOLS INCLUDE DSK1.ELAPSE \ INCLUDE DSK1.CATALOG INCLUDE DSK1.DIR INCLUDE DSK1.MORE ONLY FORTH ALSO TOOLS ALSO ASSEMBLER DEFINITIONS INCLUDE DSK1.ASM9900 INCLUDE DSK1.ASMLABELS ONLY FORTH ALSO TOOLS DEFINITIONS ALSO FORTH DEFINITIONS : NEWBOOT WARM INIT-WORDLISTS ONLY FORTH ALSO TOOLS ALSO ASSEMBLER ALSO FORTH DEFINITIONS CR CR ." Camel99 Development System." CR .FREE ABORT ; LOCK INCLUDE DSK1.SAVESYS ' NEWBOOT SAVESYS DSK7.CAMEL99DEV Supercart version Spoiler \ DEVSYS.fth build a big system on SUPERCART Forth Nov 2022 Brian Fox INCLUDE DSK1.MARKER MARKER KERNEL HEX : ?SUPERCART ['] ; A000 U> ABORT" Must use Camel99 SuperCART Forth" ; ?SUPERCART ( Test we are running in SuperCart memory now ) KERNEL \ removes ?SUPERCART test code \ Load what you need here INCLUDE DSK1.WORDLISTS INCLUDE DSK1.ANSFILES HEX : GRAPHICS ( -- ) 1 VMODE ! 0 3C0 0 VFILL 00 2 VWTR E0 DUP 83D4 C! 1 VWTR 0E 3 VWTR 01 4 VWTR 06 5 VWTR 01 6 VWTR 0 380 10 10 VFILL 8 7 VWTR 20 C/L! PAGE ; \ 83E0 LWPI, 0070 @@ B, CODE MENU 02E0 , 83E0 , 0460 , 006A , ENDCODE \ BYE will now return to E/A menu, TI-99 GPL System : BYE GRAPHICS MENU ; VOCABULARY TOOLS VOCABULARY ASSEMBLER ONLY FORTH ALSO TOOLS DEFINITIONS INCLUDE DSK1.TOOLS INCLUDE DSK1.ELAPSE INCLUDE DSK1.CATALOG INCLUDE DSK1.DIR INCLUDE DSK1.MORE ONLY FORTH ALSO ASSEMBLER DEFINITIONS INCLUDE DSK1.ASM9900 INCLUDE DSK1.ASMLABELS \ for label based jumping ONLY FORTH ALSO TOOLS DEFINITIONS ALSO FORTH DEFINITIONS : NEWBOOT WARM INIT-WORDLISTS ONLY FORTH ALSO TOOLS ALSO ASSEMBLER ALSO FORTH DEFINITIONS CR CR ." Camel99 SuperCart Development System." CR .FREE ABORT ; LOCK INCLUDE DSK1.SUPERSAVE ' NEWBOOT SUPERSAVE DSK7.SUPERFORTH A quick look at how it works: SUPERFORTH quick demo.mp4 SUPERFORTH SUPERFORTI CAMEL99DEV CAMEL99DEW 3 Quote Link to comment Share on other sites More sharing options...
GDMike Posted August 25 Share Posted August 25 4 hours ago, TheBF said: Something I always wanted for myself was a command to go back to the E/A menu rather than to the title screen. Since I have yet to make an text-file editor that I am totally happy with, this makes it much easier to use the E/A editor or Fred's improved editor as you choose. So here are couple of "scripts" than make a more complete version of Forth; more of a development version we could say. We throw in the Assembler + labels, the TOOLS and add DIR CAT and MORE (to see text file content). And we change the BYE command so it returns to the E/A menu. For that one lonely person out there who tries this system, maybe this will make life a little less painful. Here are two scripts. One that builds on the regular kernel and the 2nd builds on the Supercart Kernel. I save the binary files off to DSK7 on my Classic99 setup but you can change that to where you want it to go. I have also attached the working binary files so you can use them right away. Reveal hidden contents \ DEVSYS.fth Builds a big system on Camel99 Aug 2024 Brian Fox CR .( compiling development tools ) \ Load what you need here INCLUDE DSK1.MARKER INCLUDE DSK1.WORDLISTS INCLUDE DSK1.ANSFILES HEX : GRAPHICS ( -- ) 1 VMODE ! 0 3C0 0 VFILL 00 2 VWTR E0 DUP 83D4 C! 1 VWTR 0E 3 VWTR 01 4 VWTR 06 5 VWTR 01 6 VWTR 0 380 10 10 VFILL 8 7 VWTR 20 C/L! PAGE ; \ 83E0 LWPI, 0070 @@ B, CODE MENU 02E0 , 83E0 , 0460 , 006A , ENDCODE \ BYE will now return to E/A menu, TI-99 GPL System : BYE GRAPHICS MENU ; DECIMAL VOCABULARY TOOLS VOCABULARY ASSEMBLER ONLY FORTH ALSO TOOLS DEFINITIONS INCLUDE DSK1.TOOLS INCLUDE DSK1.ELAPSE \ INCLUDE DSK1.CATALOG INCLUDE DSK1.DIR INCLUDE DSK1.MORE ONLY FORTH ALSO TOOLS ALSO ASSEMBLER DEFINITIONS INCLUDE DSK1.ASM9900 INCLUDE DSK1.ASMLABELS ONLY FORTH ALSO TOOLS DEFINITIONS ALSO FORTH DEFINITIONS : NEWBOOT WARM INIT-WORDLISTS ONLY FORTH ALSO TOOLS ALSO ASSEMBLER ALSO FORTH DEFINITIONS CR CR ." Camel99 Development System." CR .FREE ABORT ; LOCK INCLUDE DSK1.SAVESYS ' NEWBOOT SAVESYS DSK7.CAMEL99DEV Supercart version Reveal hidden contents \ DEVSYS.fth build a big system on SUPERCART Forth Nov 2022 Brian Fox INCLUDE DSK1.MARKER MARKER KERNEL HEX : ?SUPERCART ['] ; A000 U> ABORT" Must use Camel99 SuperCART Forth" ; ?SUPERCART ( Test we are running in SuperCart memory now ) KERNEL \ removes ?SUPERCART test code \ Load what you need here INCLUDE DSK1.WORDLISTS INCLUDE DSK1.ANSFILES HEX : GRAPHICS ( -- ) 1 VMODE ! 0 3C0 0 VFILL 00 2 VWTR E0 DUP 83D4 C! 1 VWTR 0E 3 VWTR 01 4 VWTR 06 5 VWTR 01 6 VWTR 0 380 10 10 VFILL 8 7 VWTR 20 C/L! PAGE ; \ 83E0 LWPI, 0070 @@ B, CODE MENU 02E0 , 83E0 , 0460 , 006A , ENDCODE \ BYE will now return to E/A menu, TI-99 GPL System : BYE GRAPHICS MENU ; VOCABULARY TOOLS VOCABULARY ASSEMBLER ONLY FORTH ALSO TOOLS DEFINITIONS INCLUDE DSK1.TOOLS INCLUDE DSK1.ELAPSE INCLUDE DSK1.CATALOG INCLUDE DSK1.DIR INCLUDE DSK1.MORE ONLY FORTH ALSO ASSEMBLER DEFINITIONS INCLUDE DSK1.ASM9900 INCLUDE DSK1.ASMLABELS \ for label based jumping ONLY FORTH ALSO TOOLS DEFINITIONS ALSO FORTH DEFINITIONS : NEWBOOT WARM INIT-WORDLISTS ONLY FORTH ALSO TOOLS ALSO ASSEMBLER ALSO FORTH DEFINITIONS CR CR ." Camel99 SuperCart Development System." CR .FREE ABORT ; LOCK INCLUDE DSK1.SUPERSAVE ' NEWBOOT SUPERSAVE DSK7.SUPERFORTH A quick look at how it works: SUPERFORTH quick demo.mp4 16.29 MB · 0 downloads SUPERFORTH 8.13 kB · 1 download SUPERFORTI 7.88 kB · 0 downloads CAMEL99DEV 8.13 kB · 1 download CAMEL99DEW 7.13 kB · 0 downloads I still got four super carts for sale Quote Link to comment Share on other sites More sharing options...
GDMike Posted August 25 Share Posted August 25 Super forth...nice 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 25 Author Share Posted August 25 2 hours ago, GDMike said: Super forth...nice I couldn't resist. 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 26 Author Share Posted August 26 2 hours ago, GDMike said: I still got four super carts for sale How much Mike? Quote Link to comment Share on other sites More sharing options...
GDMike Posted August 26 Share Posted August 26 (edited) 75/85 each...32 KB switcheables and 8k non switcheables Edited August 26 by GDMike Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 26 Author Share Posted August 26 1 hour ago, GDMike said: 75/85 each...32 KB switcheables and 8k non switcheables I'll take a switchable. 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted August 26 Share Posted August 26 11 hours ago, TheBF said: I'll take a switchable. Just saw this...will do Quote Link to comment Share on other sites More sharing options...
+TheBF Posted September 2 Author Share Posted September 2 It always take me longer than I expected but I re-wrote Eliza using this idea of making lists of executable code rather than data. In most languages you have to make Eliza with strings. And on some of the strings you need to put a magic character at the end that means Eliza will echo back your statement as a question in something called Rogerian analysis. (I think) In the original LISP version this was not done with data but by executing LISP code. So this Forth version also uses CODE not data to create the replies. There is a word called <ROGERIAN> and you just put that in the code definition that does the reply to the users input. The code for <ROGERIAN> is : <TOPIC> INPUT$ COUNT /TAIL /CONJUGATE ; This makes the reply code look like this: (EDIT: KEY words have been added to the responses. Much clearer to read) KEY" CAN YOU" { :: CR ." Don't you believe that I can " <TOPIC?> ; :: CR ." Perhaps you would like to be able to " <TOPIC?> ; :: CR ." You want me to be able to " <TOPIC?> ; } REPLY: CANYOU KEY" CAN I" { :: CR ." Perhaps you don't want to " <TOPIC> '.' ; :: CR ." Do you want to be able to " <TOPIC?> ; } REPLY: CANI KEY" YOU ARE" { :: CR ." What makes you think I am " <TOPIC?> ; :: CR ." Does it please you to believe that I am " <TOPIC?> ; :: CR ." Perhaps you would like to be " <TOPIC> '.' ; :: CR ." Do you sometimes wish you were " <TOPIC?> ; } REPLY: YOUARE The final program loop looks like this : ELIZA ( -- ) DECIMAL .OPENING GREETING BEGIN LISTEN FIND-KEYWORD REPLY AGAIN ; I need to fix some bugs in the replies but it's a least a working program that I can fight with. Another thing different about this is that I did not use a "string package" with a string stack and all. Rather I used Forth "stack strings" along with SCAN and -TRAILING. Stack strings are processed on the data stack as an address and length pair. This lets the normal data stack fulfil the role of the string stack which saves a lot space. The current final state of the program is attached. I made use of my newish program saver that can save LOW RAM as a separate file for the E/A loader. I put the reply code down there. Program source code CAMEL99-ITC/DEMO/ELIZA/ELIZA3.2.FTH at master · bfox9900/CAMEL99-ITC · GitHub Responses code in new format CAMEL99-ITC/DEMO/ELIZA/ELIZADATA4.FTH at master · bfox9900/CAMEL99-ITC · GitHub Edit: Removed very preliminary program files. These are current ELIZA ELIZB ELIZC 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted September 7 Author Share Posted September 7 I have ignored dealing with cartridges etc. for ten years so I thought it was time to start. We always see this done in Assember, but "We don't need no stinking Assembler senor!" Here is some Forth code to put a cartridge header in super cart RAM that points to a program that starts at >A000 \ GROM header in Forth 2024 \ This is just a bit trickier in Forth because we don't have \ forward referencing. The solution is to use the data stack \ to record the address that needs to be filled later. HERE \ remember current dictionary pointer HEX 6000 DP ! \ set dictionary pointer to cartridge space \ begin compiling the header into Super-Cart space AA C, \ indicates a standard header 01 C, \ version number 00 C, \ number of programs (optional) 00 C, \ not used 0000 , \ pointer to power-up list (can't use in cartridge rom) \ Start of program chain \ HERE records the "pointer to program list" address on DATA stack. \ We will patch it later. HERE \ remember this address ( -- addr1 ) 0000 , \ ... and compile an empty pointer for now 0000 , \ pointer to dsr list (unused) \ Start of subprogram list HERE \ get the "program chain record" address 0000 , \ ... and compile zero in it as well SWAP \ reverse addresses for the store operator ( -- addr2 addr1 ) ! \ Patch address2 into address1 A000 , \ Entry point for program S" CAMEL99 FORTH" S, \ Compile program name as a counted string ALIGN \ align to even address DP ! \ restore original dictionary pointer The video shows something you have never seen before on the main menu. Cartridge Header in Forth.mp4 3 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted September 7 Share Posted September 7 (edited) 2 hours ago, TheBF said: I have ignored dealing with cartridges etc. for ten years so I thought it was time to start. We always see this done in Assember, but "We don't need no stinking Assembler senor!" Here is some Forth code to put a cartridge header in super cart RAM that points to a program that starts at >A000 \ GROM header in Forth 2024 \ This is just a bit trickier in Forth because we don't have \ forward referencing. The solution is to use the data stack \ to record the address that needs to be filled later. HERE \ remember current dictionary pointer HEX 6000 DP ! \ set dictionary pointer to cartridge space \ begin compiling the header into Super-Cart space AA C, \ indicates a standard header 01 C, \ version number 00 C, \ number of programs (optional) 00 C, \ not used 0000 , \ pointer to power-up list (can't use in cartridge rom) \ Start of program chain \ HERE records the "pointer to program list" address on DATA stack. \ We will patch it later. HERE \ remember this address ( -- addr1 ) 0000 , \ ... and compile an empty pointer for now 0000 , \ pointer to dsr list (unused) \ Start of subprogram list HERE \ get the "program chain record" address 0000 , \ ... and compile zero in it as well SWAP \ reverse addresses for the store operator ( -- addr2 addr1 ) ! \ Patch address2 into address1 A000 , \ Entry point for program S" CAMEL99 FORTH" S, \ Compile program name as a counted string ALIGN \ align to even address DP ! \ restore original dictionary pointer The video shows something you have never seen before on the main menu. Cartridge Header in Forth.mp4 3.57 MB · 0 downloads And I assume that ram disc OS could possibly be written with forth as well. Not that we need one but maybe something easier than what we do have, something a little more colorful or even 80 column for the people that have F18A Edited September 7 by GDMike 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted September 7 Author Share Posted September 7 39 minutes ago, GDMike said: And I assume that ram disc OS could possibly be written with forth as well. Not that we need one but maybe something easier than what we do have, something a little more colorful or even 80 column for the people that have F18A For sure. If ram disc can be accessed as blocks, then you can build a DOS style file system pretty easily and the Forth interpreter is the shell ... for free. Give me a spec sheet 4 Quote Link to comment Share on other sites More sharing options...
GDMike Posted September 8 Share Posted September 8 3 hours ago, TheBF said: For sure. If ram disc can be accessed as blocks, then you can build a DOS style file system pretty easily and the Forth interpreter is the shell ... for free. Give me a spec sheet @Gary from OPAmight have it 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted September 8 Share Posted September 8 (edited) 42 minutes ago, GDMike said: @Gary from OPAmight have it Well, for starters...a windowed menu Color code user data changes vs default setting Unless you just want it all to be a DOS - keyword command system. With built-in help ofptions I wouldn't mind the latter Edited September 8 by GDMike 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.