Jump to content
TheBF

Camel99 Forth Information goes here

Recommended Posts

The question becomes how long will it take to find text using SEARCH.

I wrote a test suite (by re-purposing the MORE utility, that loads a text file into an 8K buffer as contiguous text

Then using the elapsed timer I sent SEARCH looking for one of the last words in the buffer. (at byte 5669)

The screen shot shows the timing. Spoiler has the test code.

Not really speedy but not bad for the old TI-99 using a brute force algorithm.

 

 
\ TEST SEARCH IN A LARGE BUFFER

INCLUDE DSK1.TOOLS
INCLUDE DSK1.ELAPSE 

NEEDS SEARCH     FROM DSK1.SEARCH
NEEDS OPEN-FILE  FROM DSK1.ANSFILES
NEEDS VALUES     FROM DSK1.VALUES

0 VALUE LINEBUFF

CREATE FILEBUFF 2000 ALLOT

\ buffer management
VARIABLE BP
: FALLOT     BP +! ;
: FHERE      FILEBUFF BP @ + ;
: FILEBUFF$!  ( addr n -- ) TUCK FHERE SWAP CMOVE  FALLOT ;
: BUFFC,     ( c -- ) FHERE C! 1 FALLOT ;
: FBSIZE     ( -- n ) FHERE FILEBUFF - ;

1A CONSTANT ^Z

DECIMAL
: LOADFILE ( <filename>)
    80 MALLOC TO LINEBUFF
    BL PARSE-WORD DUP ?FILE
    DV80 R/O OPEN-FILE ?FILERR >R
    BEGIN
      LINEBUFF DUP 50 R@ READ-LINE ?FILERR ( -- addr n ?)
    WHILE
    \ 2DUP CR TYPE
      FILEBUFF$!  ." ."
      ?TERMINAL
      IF
         R> CLOSE-FILE  2DROP CR ." HALTED" ABORT
      THEN
    REPEAT
    R> CLOSE-FILE
    ^Z BUFFC,        \ ^Z at the end of the text
    2DROP DROP
    80 MFREE
    CR FBSIZE .  ." bytes in buffer" ;

DECIMAL

 

 

FORTHSEARCHTEST.png

Edited by TheBF
Fixed spoiler
  • Like 2

Share this post


Link to post
Share on other sites
5 hours ago, TheBF said:

I have library file for COMPARE IN Forth, that is code published by a late pioneer of Forth named Neil Baud

(also know by the pseudonym Wil Baden)

 

That is a very clever use of COUNT:o I do not believe I ever would have thought of using it that way.

 

I also do not usually think of using EXIT in the middle of a definition in that manner, but I will be keeping it in mind in the future. Of course, if I want to use it in the middle of a DO loop in fbForth, I will need to define UNLOOP , which I presume is

: UNLOOP   R> R> DROP DROP  ;

 

...lee

  • Like 1

Share this post


Link to post
Share on other sites
9 minutes ago, Lee Stewart said:

 

That is a very clever use of COUNT:o I do not believe I ever would have thought of using it that way.

 

I also do not usually think of using EXIT in the middle of a definition in that manner, but I will be keeping it in mind in the future. Of course, if I want to use it in the middle of a DO loop in fbForth, I will need to define UNLOOP , which I presume is

: UNLOOP   R> R> DROP DROP  ;

 

...lee

Ya Neil was some smart cookie.  I noticed that too. I would probably never had used COUNT but would have made a new word that did the same thing. :) 

I beleive he was involved in some kind of text analysis of books and wrote his own tools.

You can see his style here:  http://www.wilbaden.com/neil_bawd/

 

I have only recently started to realize the EXIT and factoring can allow a modest form of "un-structuring" in Forth programs (as I did with (SRCH) allowing us to branch out of a loop and land inside another word. Some might call it cheating. 

 

 

 

  • Like 1

Share this post


Link to post
Share on other sites
13 minutes ago, TheBF said:

I have only recently started to realize the EXIT and factoring can allow a modest form of "un-structuring" in Forth programs (as I did with (SRCH) allowing us to branch out of a loop and land inside another word. Some might call it cheating. 

 

I do not see why. You are not actually branching into the middle of a word, you are terminating the current word early and landing where you would have with a normal completion, anyway. Maybe it is not intuitively obvious to the casual observer, but I certainly would not even think of calling it cheating. :)

 

...lee

  • Haha 1

Share this post


Link to post
Share on other sites

Not that you really need it but I made unloop in CODE and it is part of my DO LOOP system, but can be called separately as well.

I waffled on the big version or the smaller 1 instruction version.

CODE: UNLOOP
              RP  4 ADDI, ( *RP+ *RP+ CMP,) 
              NEXT,
              END-CODE

 

Edited by TheBF
Added the word You.
  • Like 1

Share this post


Link to post
Share on other sites
16 minutes ago, Lee Stewart said:

 

I do not see why. You are not actually branching into the middle of a word, you are terminating the current word early and landing where you would have with a normal completion, anyway. Maybe it is not intuitively obvious to the casual observer, but I certainly would not even think of calling it cheating. :)

 

...lee

By using a separate word and EXITing it is like I jumped over 1 /STRING and the REPEAT word which puts me back inside SEARCH. 

 

I have not built these "quotations" that I read about in comp.lang.forth but I believe they would allow me to do what I did without putting the word (SRCH) in the dictionary. Something like this:

HEX
: SEARCH ( c-addr1 u1 c-addr2 u2 -- c-addr3 u3 flag )
          100 DUP >R MALLOC TO SBUFF
       [: SBUFF PLACE    ( QUOTATION starts here)
          BEGIN
             DUP
          WHILE
             SBUFF COUNT
             2OVER SAMELEN COMPARE
             0= IF  EXIT THEN      \ NOW EXIT jumps to ;]
             1 /STRING
          REPEAT ;]
         DUP 0>
          R> MFREE
          0 TO SBUFF ;

Here it is clearer to see that I am doing a GOTO :) 

 

I may have to try and create these quotation things...

 

  • Like 1

Share this post


Link to post
Share on other sites
11 minutes ago, TheBF said:

Not that really need it but I made unloop in CODE and it is part of my DO LOOP system, but can be called separately as well.

I waffled on the big version or the smaller 1 instruction version.

 

The ADDI, is definitely faster than CMP, by 16 clock cycles and 3 memory accesses!  What was your more-than-1-instruction version?

 

...lee

Share this post


Link to post
Share on other sites
Just now, Lee Stewart said:

 

The ADDI, is definitely faster than CMP, by 16 clock cycles and 3 memory accesses!  What was your more-than-1-instruction version?

 

...lee

I mis-spoke, I should have said 2 CELLs vs 1 CELL version.

  • Like 1

Share this post


Link to post
Share on other sites

Just to round off the discussion of quotations, I found this document and I think the code is very possible for Camel Forth.

http://www.forth200x.org/quotations.txt

 

Implementation
--------------
It is not possible to define quotations in ISO Forth. The following
is an outline definition, where SAVE-DEFINITION-STATE and RESTORE-
DEFINITION-STATE require carnal knowledge of the system and are left
to the implementor.

: [: ( c: -- quotation-sys colon-sys )
  postpone ahead save-definition-state :noname ; immediate
  
: ;] ( c: quotation-sys colon-sys -- ) ( s: -- xt )
  postpone ; >r restore-definition-state
  postpone then r> postpone literal ; immediate

 

Share this post


Link to post
Share on other sites

OK, that makes my head hurt a little. I guess that construct somehow compiles what is within [: ... ;] as a word with no header such that the effect is the same as before, but saving the 10 bytes for a header in the process.

 

...lee

  • Like 1

Share this post


Link to post
Share on other sites
2 minutes ago, Lee Stewart said:

OK, that makes my head hurt a little. I guess that construct somehow compiles what is within [: ... ;] as a word with no header such that the effect is the same as before, but saving the 10 bytes for a header in the process.

 

...lee

Exactly.  And if you read the paper they talk about dealing RECURSE and DOES> and that makes my head hurt.  I would just make those two illegal along with ;CODE

:) 

 

Topic Shift

 

I was just looking at Neil's page after a long time away and found this:

\  The traditional definition for  (.)  is:

\ : (.)                            ( n -- str len )
\     dup ABS 0 <#  #S  ROT SIGN  #> ;

\  But we like to see TRUE and SIGN-BIT in hex as FFFFFFFF and
\  80000000, and also see bit masks as unsigned.  It would be nice to 
\  let the program do it.

\ : (.)                            ( n -- str len )
\     BASE @ 10 = IF  dup ABS  ELSE  0 SWAP  THEN
\     0 <#  #S  ROT SIGN  #> ;

\  Even nicer would be the following.  This helps in distinguishing
\  similar binary numbers.  Like 0FFFFFFF from FFFFFFFF and 08000000
\  from 80000000.

: (.)                             ( n -- str len )
    CASE BASE @
    10 OF  dup ABS 0 <# #S ROT SIGN #>                       ENDOF
    16 OF          0 <#  BEGIN  # #  2dup OR 0= UNTIL #>     ENDOF
     2 OF          0 <#  BEGIN  # # # #  2dup OR 0= UNTIL #> ENDOF
                   0 <#  #S #>
     0 ENDCASE ;

\  Of course we want with whichever one...

: .  ( n -- )  (.) TYPE SPACE ;
 

I think I am going to add the big version to my programmers tools file with .S and DUMP and the like.

I hate seeing signed HEX numbers and having to use U. all the time to print them when debugging stuff.

He also uses this to define .S 

 

 

  • Like 2

Share this post


Link to post
Share on other sites

After seeing the VDP I/O performance of FbForth in the sevens problem topic I decided to dig in on what I could do to improve text writing speeds while at the same time keeping the hi-level code in Forth for instructional purposes. I had a faster scroll in place but that was only part of the issue.  The Forth word TYPE was written in Forth and although  the word that wrote to the VDP memory (VC!) was Assembler,  Forth was used to update the column variable, detect the end of screen and then to call SCROLL.

I wanted to see how much things could move faster if I improved TYPE but I didn't want to re-write everything in Assembler because it probably would take more space and my 8K limit was looming.  How about removing setting up the address for each character and leverage the VDP hardware's auto-increment capability?

I gave Forth direct access to the set the VDPWA which disables interrupts and is a callable routine in the kernel.

Then the mate to it is VEMIT  which writes a character, increments the VCOL variable and returns the new VCOL. 

 

New Assembler routines: 

Note on STWP below. System (USER) variables are located below the workspace so that each task can have its own copy.

No. $34 is VCOL (video column) variable

\ *NEW*  For faster TYPE routine in Forth
CODE: VDPWA! ( Vaddr -- )
             TOS R0 MOV,
             WMODE @@ BL,
             TOS POP,
             NEXT,
             END-CODE

\ VEMIT writes to VDP address set by VDPWA! and updates VCOL user variable
\ VDP auto increments the address for faster character output
CODE: VEMIT ( char -- column')
             TOS SWPB,
             TOS VDPWD @@ MOVB,  \ write char to vdp data port
             R1     STWP,
             34 (R1) INC,        \ Bump  VCOL user variable
             34 (R1) TOS MOV,    \ fetch VCOL to TOS
             NEXT,
             END-CODE

By returning the VCOL value it can be passed to the conditional newline routine called ?CR. 

If VCOL >= chars-per-line ,  ?CR calls the newline routine CR. 

CR resets VCOL and increments VROW.

If VROW >= lines-per-screen it calls SCROLL.

With all this in place the new faster TYPE routine reduces to:

: CR   ( -- )
          PAUSE
          VCOL OFF  VROW 1+!
          VROW @ L/SCR 1- >  IF  SCROLL  THEN  ;

: ?CR    ( column -- ) C/[email protected] 1- > IF  CR  THEN ;

: TYPE   ( adr cnt -- ) VPOS VDPWA!  
         BOUNDS ?DO  I [email protected] VEMIT ?CR   LOOP ;

Using Lee's version of the Seven's Problem the slowest combination of SCROLL and TYPE ran in 1:25 on Camel99 Forth.

(FbForth time is:  0:53 seconds using the ELAPSE timer)

 

Using the fast scroll and and fast type the program ran in 1:06.  That's a 29% improvement. :)

I still have 50 bytes left in the kernel for an emergency change so V2.54 is going to be the current system.

 

I can't beat an I/O driver written with integrated Assembler scroll and newline but I got much closer.

 

Edit: I also had to add one line to SCROLL to set the VDPWA to the bottom line to make this all work.

 

 

 

Edited by TheBF
  • Like 1

Share this post


Link to post
Share on other sites

It was all fun and games trying to make a faster VDP I/O system for the sevens problem, but I forgot one little detail...

 

CAMEL99 Forth is supposed to Support multi-tasking. :)

 

So if you set the VDP write address, write a character and then change tasks and that new task does some screen I/O guess what happens?

Now I could control for that but forcing screen typing to hog the system until it completes everything including newlines and screen scrolls but that makes other tasks "lumpy" in the way they run so I have reverted. For a cooperative Forth tasker it's best that the fundamental I/O operation does a task switch so in this case I have a low level (EMIT) word that does the job.  It also shaved 42 bytes out the system by not having the faster TYPE routine.  I can live with it.

 

I did find one thing that makes a speedup and that is a word I call '[email protected]'   It increments a variable and fetches the new value in one word. This makes screen variable management for row and column quite a bite faster as the fastest way to do it in Forth is:

 

\ Standard Forth options
 1 VCOL +!  VCOL @ 
   VCOL 1 OVER +! @ 

\ Versus
 VCOL [email protected]  

Time to watch the Grey Cup, Canadian football final game. 

 

  • Like 1

Share this post


Link to post
Share on other sites

CAMEL99 Forth V2.5  Release

 

I am a poor administrator but I have finally committed to one version of the kernel program CAMEL99. It seems pretty stable now but I am the only one who has used it.

Attached is a ZIPped up version of DSK1. to make it simple to try it out. The biggest difference between CAMEL99 and the other TI-99 Forths out there is the that CAMEL99 attempts to be ANS/ISO compliant. Not a big deal for hobby coding but there it is.

 

If you want to try it mount the DSK1 in the ZIP file to Classic99, use Editor/Assembler cartridge and select E/A 5.  Program name is DSK1.CAMEL99.

"COLD" <enter> restarts the system.

 

Try INCLUDE DSK1.COOLSPRITE to compile a demo program with libraries.

(It takes 36 seconds to compile the approximately 480 lines of code at normal speed)

Type RUN to make it go. BREAK will stop it.

Type BYE to exit.

 

A slightly updated version of the instruction manual (mostly typo corrections) will be up there some time later today.

I realize Forth doesn't win popularity contests :) but if anybody wants to try it I am happy to answer any and all questions.

 

### Nov 28, 2019  V2.5
Indirect Threaded Version
- Settled on one build of CAMEL99 Forth. All variations are removed.
- 25% speed up of CREATE DOES> structures by using BRANCH & LINK instruction
- Fixed DSK1.ANSFILES file handle bug. Errors did not release current file handle.
- Improved VDP screen driver using [email protected] code word
- Improved DSK1.VALUES. Faster TO and +TO
- Cleaned up LIB.ITC. TI99 versions are in DSK1.
- Added DSK1.TRAILING. (-TRAILING -LEADING TRIM)
- Added DSK1.HEXNUMBER. H# is a prefix word to interpret numbers as radix 16.
- DSK1.TOOLS now includes VDUMP for VDP ram and SDUMP code for SAMS card.
  (HEX and BINARY numbers alway print unsigned after tools are loaded.)
- DSK1.CODEMACROS provides native 9900 indexed addressing arrays.
- DSK1.VTYPE improved VTYPE updates VCOL. AT" ( x,y) placing text.
- DSK1.AUTOMOTION provides Automatic sprite motion like Extended BASIC


### Known BUG
- When INCLUDE is used for a file on a disk other than DSK1, the library files will try to load from that same disk.  Investigating our FILESYSX for the problem.
- Temporary fix is to keep programs on disk one or load libraries manually before loading a program from DSK2 or DSK3.

 

DSK1.zip

Edited by TheBF
No bug when loading from DSK2. Not tested on hardware
  • Like 1

Share this post


Link to post
Share on other sites

I am reviewing, running and editing, when necessary, all the DEMO programs on GITHUB.

https://github.com/bfox9900/CAMEL99-V2/tree/master/DEMO

 

It looks like V2.5 is a better multi-tasking kernel.

I was able to run 43 total tasks. (42+console)  This consumes almost the entire lower 8k for the task blocks.

Version 2.x Would only do 30 tasks and then it would blow up.

 

Each task is just running this:

HEX 10 CONSTANT STKSIZE

VARIABLE X   \ used to test if tasks are running

: DROPS   ( n --)  0 DO DROP PAUSE LOOP ; \ drop items from the stack

: STKTHING   \ fill and clear data stack so it can be seen in debugger
          BEGIN
            STKSIZE 0 DO PAUSE DEAD  LOOP
            STKSIZE DROPS

            STKSIZE 0 DO PAUSE BEEF  LOOP
            STKSIZE DROPS
            1 X +!
          AGAIN ;

 

Share this post


Link to post
Share on other sites

On the outside chance that anyone cares... :)  there is an updated version of the Manual for CAMEL99 Forth.

https://github.com/bfox9900/CAMEL99-V2/blob/master/DOCS/Camel99 for TI-BASIC Programmers Rev1.9.pdf

 

Or if you would rather something smaller, there is a list of the library files with one liner of what they do. (Extracted from the Manual)

https://github.com/bfox9900/CAMEL99-V2/blob/master/DOCS/Camel99 Forth Library files.pdf

 

The most complete list of lib files in TI-99 format will always be here:

https://github.com/bfox9900/CAMEL99-V2/tree/master/DSK1.ITC

 

Some font files are here:

https://github.com/bfox9900/CAMEL99-V2/tree/master/DSK3

 

 

  • Like 1

Share this post


Link to post
Share on other sites

So here is something that I don't understand. (A common motif) :)

 

I went looking for a bug that I found just recently where I try to load a file on DSK3.

 

The file on DSK3. begins to load, but the file has "nested" statements that say INCLUDE DSK1.FILE#1,  INCLUDE DSK1.FILE#2 etc

Even though the statements specify DSK1 for the extra includes, the system goes looking for FILE#1 and FILE#2 on DSK3.  Hmm...

 

However when I put the identical file on DSK2. it begins to load and loads the nested includes from DSK1. as the statements in the file request.

 

The screen shot shows what I am trying to describe.  You can see the failure including DSK3.SAMSDEMO at the top.

Next I load DSK2.SAMSDEMO and it moves through the 5 lib files on DSK1 first before completing the load on DSK2.

 

Is there something different about DSK3 that makes it unique compared to DSK1 and DSK2 ?

 

 

dsk3nestedfilebug.png

Share this post


Link to post
Share on other sites

What da' ya' mean you want another stack?

 

Forth already has two stacks.  Why would you ever need another one?  Well a stack is a handy data structure. I am working on a demonstration of the shunting yard algorithm to convert infix math to rpn. For that I need to keep a stack for the brackets and operators.  I looked at some code from my very old HsForth system and modified it for Camel99 Forth.  I have not tried it on other TI-99 Forth systems but it should not be too hard to adapt.  You might need to add these definitions:

: [email protected] ( adr -- n1 n2 )   DUP 2+ @ SWAP @ ;
: CELL+  ( n -- n') 2+ ;

 

The word to create the data structure here is called LIFO:   ( last in first out) which is a fancy name for a stack. You might prefer the word stack.

LIFO:  creates 3 integer locations to manage the stack.  The PUSH and POP words have built in error detection which can save your bacon in a real program.

 

Spoiler removed until bugs are killed...

 

 

 

 

LIFODEMO.png

Edited by TheBF
  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, TheBF said:

The word to create the data structure here is called LIFO:   ( last in first out) which is a fancy name for a stack. You might prefer the word stack.

LIFO:  creates 3 integer locations to manage the stack.  The PUSH and POP words have built in error detection which can save your bacon in a real program.

 

2-! looks like a victim of cut-n-paste. It should read

: 2-!  ( addr -- ) DUP @ 2- SWAP ! ;

 

A couple of other items:

  • I think the comments for the first two items are confusing. The stored numbers are the number of bytes rather than the number of cells.
  • Does the stack pointer slot have a future purpose not obvious from the current code?

I definitely like the idea of a protected stack. Very nice!

 

...lee

Share this post


Link to post
Share on other sites

There are some big bugs here I made it work for for my other project and was happy to have a user stack. :)

 

Sincere apologies.  I will work it over.

 

 

Share this post


Link to post
Share on other sites
2 hours ago, TheBF said:

There are some big bugs here I made it work for for my other project and was happy to have a user stack. :)

 

Sincere apologies.  I will work it over.

 

Don’t mind me! Certainly no apologies are necessary. I was, mayhap, a bit too quick on the uptake. Part of this is just my wish to insure that we avoid confusing the host of passersby! |:)

 

...lee

  • Like 1

Share this post


Link to post
Share on other sites
23 hours ago, Lee Stewart said:

 

2-! looks like a victim of cut-n-paste. It should read

: 2-!  ( addr -- ) DUP @ 2- SWAP ! ;

 

A couple of other items:

  • I think the comments for the first two items are confusing. The stored numbers are the number of bytes rather than the number of cells.
  • Does the stack pointer slot have a future purpose not obvious from the current code?

I definitely like the idea of a protected stack. Very nice!

 

...lee

Fixed the bug with 2-!.  It was the real culprit. Thanks.

I fixed the comment changing cells to bytes.

 

I went back into the archives of old HsForth files, circa 1990ish, and discovered that the comment I used for the stack pointer was only in my re-write and that in fact it looks like Jim added an extra cell with a zero in it for unknown reasons. Perhaps it is a safety zone? 

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Searching Faster

 

I revisited my search code and I had needlessly complicate my conversion of the S= ("string equals") code routine in Camel Forth to the ANS standard word COMPARE.

I then improved the definition of 2OVER by using  "4TH" a little routine borrowed from Neil Bauds toolbox that picks the 4th stack item and pushes onto the top of the stack.

Due to the 9900 instruction set this Forth command is the same speed as OVER. So  "4TH 4TH" makes a very fast 2OVER word.

 

For maximum speed I rolled these up as text macros. Arguably they should be state smart but since they will be embedded in an editor I am going to leave them as is.

 

Bottom Line:   This version does the same search of DSK1.ASM9900 in 4.48 seconds vs 7 seconds.

 
 \ search.fth   Forth 2012 word
\  INCLUDE DSK1.TOOLS
 NEEDS VALUE FROM DSK1.VALUES
 NEEDS 3RD   FROM DSK1.3RD4TH

\ S= is CAMEL Forth primitive
: COMPARE  ( a1 n1 a2 n2 -- -1|0|1 ) S" ROT MIN S=" EVALUATE ; IMMEDIATE
: 2OVER   ( d d2 -- d d2 d) S" 4TH 4TH" EVALUATE ; IMMEDIATE

0 VALUE SBUFF     \ temp buffer for search string
: (SRCH)  ( c-addr1 u1 c-addr2 u2 -- c-addr3 u3)
          SBUFF PLACE
          BEGIN
             DUP
          WHILE
             SBUFF COUNT
             2OVER COMPARE
             0= IF EXIT THEN    ( jump to ';')
             1 /STRING
          REPEAT
;

HEX
: SEARCH ( c-addr1 u1 c-addr2 u2 -- c-addr3 u3 flag )
          100 DUP >R MALLOC TO SBUFF
         (SRCH) DUP 0>
          R> MFREE
          0 TO SBUFF ;

 

 

searchfaster.png

Edited by TheBF
  • Like 1

Share this post


Link to post
Share on other sites

Using Multiple Screens in Camel99 Forth

While reviewing the VDP Programmers Guide I got more familiar with the internal register functions. It seemed obvious after that, that it was possible to have different "screens" for text in the Camel Forth environment. When I implemented it I discovered a gap in my VDP driver.  I had built a variable called VTOP that held the VDP address of the top line but I had assumed that the the screen always began at VDP address >0000. (oops)

 

I had to add one instruction to my VDP address calculator routine which takes the column and row to do the computation. 

( the row variable is called VROW and it is a USER variable, meaning it is based on the workspace address. That's why the 2E (R1) is used below)

I just needed to add the value of VTOP as an offset into VDP RAM in the last line of code.

CODE: >VPOS ( col row -- vaddr) \ compute video address
             R1         STWP,
             TOS     R3  MOV,   \ this move to make best use of MPY
             2E (R1) R3  MPY,   \ multiply by chars/line. result goes to R4 ie: TOS
            *SP+     TOS ADD,   \ add col value to TOS
             VTOP @@ TOS ADD,
             NEXT,
             END-CODE

I had also made this assumption in my scroll routine ie: The end of the screen was always equal to the no. of bytes in a screen since the screen started at >0000.

Mistake. So I had to fix that as well.

: SCROLL ( -- )
          PAUSE                \ addresses on stack
          H @                  ( -- heap)
          VTOP @ DUP C/[email protected] +    ( -- heap 1stline 2ndline )
          SWAP C/SCR @ + SWAP  ( -- heap 2ndline lastline )
          DO
             I  ( -- heap 2ndline lastline)
             OVER 2DUP    C/[email protected] VREAD
             SWAP C/[email protected] -  C/[email protected] VWRITE
          C/[email protected] +LOOP
          0 17 CLRLN
          DROP
; 

With those changes made it THEN became simple to do this:

(Edit: Updating VP was wrong)

\ Multiple screens in 40 Column mode
HEX
: SCREEN ( n -- ) 400 * VTOP ! ;

: SCREEN: ( scr# fg bg -- )
     SWAP 4 LSHIFT +      \ combine color values to a byte

     CREATE  ( color) , ( scr#) ,
             400 VP +!  \ allocate 1K vdp ram

     DOES> [email protected] 7 VWTR  \ set color
           DUP SCREEN  2 VWTR ;

\ DEMO code
\ Vpage fg  bg
\ ----- --  --
\   0    1   7 SCREEN: SCR0
\   4    1   3 SCREEN: SCR4
\   5    1   4 SCREEN: SCR5

 

The word SCREEN lets you choose a different screen as the destination for the VDP device driver. (EMIT TYPE CR etc.)

The VDP chip takes the value of register 2 * >400 and puts text at that address. So SCREEN calculates that and stores it in VTOP.

The second line just updates the VP variable which Camel99 Forth uses to keep track of how much VDP RAM is in use.

 

The word SCREEN:  lets you define words that hold  the color and the screen number. 

When these words are executed they automatically change the screen and the color.

I did not make an attempt to remember the col and row values for each screen, but this could be done. 

I also did not try to protect the programmer from stomping on SCREEN 1,2 or 3 which hold the pattern tables etc. in CAMEL99 Forth TEXT mode.

:)  You have been warned.

 

The video shows the code in action. It is possible to create upto 8 extra screens but if you need 3 files open at the same time you will be limited to 7. (I think)

If you choose to re-organize the VDP space you could also the control data to the top of VDP and keep the text screens contiguous. I may go there in future.

 

I used this idea to make a 40 column editor that loads a file into VDP ram and is edited like Forth BLOCKs. Not sure it is all that practical but it works.

 

 

 

 

Edited by TheBF
Changed video to MP4
  • Like 1

Share this post


Link to post
Share on other sites

Once upon a time a guy tried to write a Forth kernel.  Since he had all the source code he decided to change it... a lot.

 

I was scratching my head about how my "totally cool" :)  repeating key routine kept randomly plopping a random key on the screen.

The method was based on code from the Tech pages but converted to Forth. How could it possibly be incorrect? 👨‍🎓

Turns out a few generations back I re-organized how my KSCAN interface worked. I used to read the char buffer in Forth after calling KSCAN.

 

Somewhere in the past I decided to change that and just grab the character in Assembler if there was one to grab. However I neglected to update where I re-enabled interrupts. 

 

This made no problems until I was grabbing keys at high speed.

 

Good news is that I found it.  I will be publishing a new version very soon.

I have re-organized some of the source code to try and make clearer.

\ BAD VERSION
CODE: KEY? ( -- ?) 
            TOS PUSH,
            TOS CLR,            \ TOS will be our true/false flag
            0 LIMI,
            83E0 LWPI,          \ switch to GPL workspace
            000E @@ BL,         \ call ROM keyboard scanning routine
            2 LIMI,             \ ** ERROR WAS HERE *** 
            WRKSP0 LWPI,        \ return to Forth's workspace , interrupts are restored
            837C @@ R0 MOVB,    \ read GPL status byte (=2000 if key pressed)
            NE IF,
                8374 @@ TOS MOV, \ read the key
            ENDIF,
            NEXT,               \ return
            END-CODE

\ NEW VERSION
CODE: KEY? ( -- ?) 
            TOS PUSH,
            TOS CLR,            \ TOS will be our true/false flag
            0 LIMI,
            83E0 LWPI,          \ switch to GPL workspace
            000E @@ BL,         \ call ROM keyboard scanning routine
            WRKSP0 LWPI,        \ return to Forth's workspace
            837C @@ R0 MOVB,    \ read GPL status byte (=2000 if key pressed)
            2 LIMI,             \ ** THE FIX *** 
            NE IF,
                8374 @@ TOS MOV, \ read the key
            ENDIF,
            NEXT,               \ return
            END-CODE

  

 

 

 

 

 

Edited by TheBF
fixed comment
  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...