Jump to content
IGNORED

Camel99 Forth Information goes here


TheBF

Recommended Posts

So with the RKEY fixed the TINYED program works much better albeit with minimal features.

It loads the file into VDP "screens". You then edit page by page like a Forth block editor.

With this restriction and the 40 column limit it makes for rather "squished" source code which I am not a big fan of it.

However the editor can edit itself with a few lines to spare. :)

 

 

( TINYED 9k file size, 40 cols BJF 2019 )
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
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 ;
: UPDT ( -- addr) VTOP @  3FE + ;
: TOUCH   ( -- )  TRUE  UPDT V! ;
: VIRGIN   ( -- ) FALSE UPDT V! ;

\ variables ...
VARIABLE INSERTING
VARIABLE TOPLINE
VARIABLE OFFSET
VARIABLE SCR
: GETXY  ( -- col row) VROW 2@ ;
CREATE FORTHXY  0 , 0 ,
CREATE EDITXY   0 , 0 ,
CREATE FILENAME   10 ALLOT
DECIMAL
: CUP    VROW @ 0 > IF VROW 1-!
         ELSE HONK THEN ;
: CDOWN  VROW @ 23 < IF VROW 1+!
         ELSE HONK THEN ;
: CRIGHT VCOL @ 39 < IF VCOL 1+!
         ELSE HONK THEN ;
: CLEFT  VCOL @ 0 > IF VCOL 1-!
         ELSE HONK THEN ;
: 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
: VBLOCK ( n -- vaddr) 4 + 1K * ;
: TOUCHED? ( scr# -- ?)
         VBLOCK 3FE + V@ ;
: ]LINE ( ndx -- addr)
         C/L@ * SCR @ VBLOCK + ;
\ return vdp address of current row
: VLINE ( -- VDPaddr)  VROW @ ]LINE ;

\ select VDP screen to DISPLAY
: ACTIVE ( scr# -- ) 4 +  2 VWTR ;
: SCRCOLOR (  -- )
  SCR @ TOUCHED? IF   LTGRN
  ELSE GREEN THEN ;

: SCREEN ( n --)
     0 9 CLIP SCR !   0 ]LINE VTOP !
     SCR @ ACTIVE SCRCOLOR ;

: RIGHTSIDE ( -- VDPaddr len)
     VLINE C/L@  VCOL @ /STRING ;

: LEFTSIDE  ( -- VDPaddr len)
      VLINE VCOL @ 1+ ;
\ copy vdp string to RAM address
: VCOPY ( vaddr len addr --)
      SWAP VREAD ;
\ text manipulation
: DELCHAR    ( -- )
     RIGHTSIDE 1 /STRING TUCK
     PAD VCOPY
     PAD VPOS ROT VWRITE ;

: PUSHRIGHT ( -- )
     RIGHTSIDE TUCK
     PAD VCOPY
     BL VPUT
     PAD VPOS 1+ ROT 1- VWRITE ;
HEX
: FORTH ( -- ) \ goto forth console
    0 02 VWTR
    VTOP OFF
    CYAN
    GETXY  EDITXY 2!
    FORTHXY 2@ AT-XY
    ULINE CURS !
    ABORT ;

HEX
: PURGE  ( -- )
  09 0 DO
    I VBLOCK 1K BL VFILL VIRGIN
  LOOP
  9 VBLOCK C/SCR @ BL VFILL VIRGIN
  SCR OFF ;

VARIABLE HNDL
: OPEN  ( adr len fam -- )
   OPEN-FILE ?FILERR HNDL ! ;

: CLOSE
   HNDL @ CLOSE-FILE HNDL OFF
   ?FILERR ;

: READ  ( addr len -- addr len')
  HNDL @ READ-LINE ?FILERR DROP ;

: WRITE ( addr len -- )
  HNDL @ WRITE-LINE ;

HEX
: LOADSCR ( -- eof)
  0
  L/SCR 0
  DO
    HNDL @ EOF
    IF DROP TRUE  LEAVE
    ELSE
      PAD DUP 50 READ
      I ]LINE SWAP VWRITE
    THEN
  LOOP ;

: LOADFILE ( addr len --)
    PURGE
    DV80 R/O OPEN
    SCR OFF
    BEGIN
      LOADSCR ( -- ?)
      SCR 1+!
    UNTIL
    CLOSE ;

: SAVESCR  ( n -- )
    SCR !
    L/SCR 0
    DO I ]LINE C/L@ PAD VCOPY
       PAD C/L@ -TRAILING WRITE
       IF  FORTH ." Write error" THEN
    LOOP ;

: SAVEFILE ( addr len --)
    DV80 R/W OPEN
    0A 0 DO
         I TOUCHED?
         IF I SAVESCR  THEN
    LOOP
    CLOSE  SCR OFF  ;

: TINK     ( -- )
  80 SND! 2 SND!  92 SND! 25 MS
  9F SND! ;

: PGDWN ( )
  SCR @ DUP 0 = IF DROP HONK
  ELSE 1- SCREEN  TINK   THEN ;

: PGUP   ( )
  SCR @ DUP 9 = IF DROP HONK
  ELSE 1+ SCREEN  TINK   THEN ;

HEX
: KEYHANDLER ( char -- )
CASE
02 OF PGDWN  ENDOF 03 OF DELCHAR ENDOF
04 OF INS/DEL ENDOF 0C OF PGUP  ENDOF
0D OF NEWLINE ENDOF 08 OF CLEFT ENDOF
0B OF CUP     ENDOF 0A OF CDOWN ENDOF
09 OF CRIGHT  ENDOF
90 OF VIRGIN PAGE ENDOF
93 OF TOUCH ENDOF 95 OF VCOL OFF ENDOF
96 OF C/L@ 1- VCOL ! ENDOF
0F OF FORTH  ENDOF
  HONK  ( unknown key)
ENDCASE ;

DECIMAL
: EDIT ( scr# -- )
    DUP 0 9 BETWEEN
    INVERT ABORT" Bad screen#"
    SCR !
    VMODE @ 2 <> IF TEXT THEN
    GETXY FORTHXY 2!  INSERTING OFF
    ULINE CURS !   SCR @ SCREEN
    0 0 AT-XY
    BEGIN
      RKEY DUP ALPHA?
      IF INSERTING @
         IF   PUSHRIGHT
         THEN VPUT CRIGHT TOUCH
      ELSE KEYHANDLER
      THEN SCRCOLOR
    AGAIN ;

 

Edited by TheBF
Link to comment
Share on other sites

On 11/29/2019 at 11:47 AM, TheBF said:

CAMEL99 Forth V2.5  Release


### 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 109.09 kB · 5 downloads

This was not a bug in Camel99 Forth. It was my wrong setup of Classic99 causing DSK3 to be replaced by DSK1.

So this is resolved.

  • Like 1
Link to comment
Share on other sites

One of things I remember fondly about programming DOS was the multiple screens that you could use in the graphics memory.

Now that I have figured out how to do this on the TI-99 I am going to change the CAMEL99 kernel to provide this feature while remaining

backwards compatible if you only use the default display screen.

 

I have added a new variable called VPG (vdp page).  This variable contains the offset into VDP RAM where the screen resides, simply a multiple of >400.  The VPG variable is used to calculate VDP screen addr with col & row and provides a block offset into VDP RAM for that calculation. 

 

A new word has been added called TOPLN, which returns the VDP address of the top line on the screen, calculated by adding the variables VPG+VTOP.

 

Setting VTOP to something other than zero allows you to create a protected portion of the screen at the top that will not be cleared by PAGE.  For now I have decided that this is enough "windowing" for my requirements and so I don't have a VEND variable in the system.

 

The SCREEN allows the use of any 1K boundary to be used as the display memory to allow flexibility in different modes.

So in the code below SCREEN 1,2 and 3 cannot be used because the patterns descriptor table etc. are located there.

If you move these things around SCREEN will let you set the display memory where you want it.

 

So the library file to provide names screens becomes:

\ Multiple named screens in 32 or 40 Column mode
HEX
: SCREEN ( n -- ) 400 * VPG ! ;

: SCREEN: ( scr# fg bg -- )
     SWAP 4 LSHIFT +   \ colors to byte
     CREATE  ( color) , ( scr#) ,
             400 VP +!  \ allocate 1K vdp ram
     DOES> 2@ 7 VWTR  \ set color
           DUP SCREEN  2 VWTR ;

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

I will be beating this up and will get the code up on Github early in the new year.

 

  • Like 1
Link to comment
Share on other sites

I have been seeing what could be done to improve my lack-lustre scroll routine without resorting to writing it all in Assembler.

I took a Forthy approach and did one with no loops.  :)  Turns out it is not much faster that what I had before but it was good to see the limits of what could be done.

 

The new one is a little quicker than before and by using a feature of the cross compiler I removed the text labels of the MOVUP and the CHUNK word from the EA5 program image so that saves a few bytes.  I am using a 4 line buffer controlled by CHUNK.  I don't think I can get it faster without using all assembler. The speed is about 48mS with a 4 line buffer and it only drops to ~46mS with a buffer the size of the screen.

 

I also made use of inlining kernel words to make CHUNK faster. I haven't changed the cross-compiler for over a year so it's pretty stable.

 

       [PRIVATE]
CODE: CHUNK  ( -- n) CODE[ C/L@ 4* ]  NEXT, END-CODE

: MOVEUP ( vaddr -- 'vaddr)
         H @ OVER C/L@ +  ( -- 1stline heap 2ndline)
         OVER CHUNK VREAD
         OVER CHUNK VWRITE
         CHUNK + ;
        [PUBLIC]

: SCROLL ( -- ) \ 4.58 / 100 scrolls
         TOPLN
         MOVEUP MOVEUP MOVEUP MOVEUP MOVEUP MOVEUP
         DROP
         0 17 CLRLN
;

 

  • Like 1
Link to comment
Share on other sites

After the scroll exercise I wondered if there was anything I could do with VREAD and VWRITE. 

How about move 2 bytes at a time and loop 1/2 as many times?

 

Turns out it seems to work.  It improved the scroll time from 47mS to 40mS, a 17% improvement.

I divide the loop counter and then align it to an even value. It costs 20 bytes.

I think I will make the counter massaging into a sub-routine to save a few bytes.

 

EDIT: Unfortunately I am seeing errors in some graphics programs unsurprisingly.

        I may just try an assembler scroll version that uses these concepts.

CODE: VREAD   ( VDP-adr RAM-addr cnt  -- )
              R1 POP,              \ pop buffer addr. to R1
              R0 POP,              \ pop VDP addr. to R0
              R3 VDPRD LI,         \ put the VDP read port address in R3. 12.9% faster
              RMODE @@ BL,         \ call the RMODE control routine
              TOS  1 SRA,          \ divide count by 2
              TOS INCT,
              TOS -2 ANDI,          \ align to even address boundary
              BEGIN,
                *R3 *R1+ MOVB,     \ READ char from VDP RAM into CPU RAM , autoINC
                *R3 *R1+ MOVB,     \ READ 2nd char
                 TOS DEC,          \ dec the counter
              EQ UNTIL,            \ jump back if carry flag not set
              TOS POP,             \ refill TOS
              NEXT,
              END-CODE
              
CODE: VWRITE  ( RAM-addr VDP-addr cnt -- )
             R0 POP,              \ vaddr to R0
             R1 POP,              \ cpu addr to R1
             TOS TOS MOV,         \ protect from cnt=0 like CMOVE
             NE IF,
                 WMODE @@ BL,
                 R3 VDPWD LI,      \ vdp addr. in a reg. makes this 12.9% faster
                 TOS  1 SRA,       \ divide count by 2
                 TOS INCT,
                 TOS -2 ANDI,      \ align to even address boundary
                 BEGIN,
                   *R1+ *R3 MOVB,  \ write byte to vdp write port
                   *R1+ *R3 MOVB,  \ write 2nd byte
                    TOS DEC,       \ dec the byte counter
                 EQ UNTIL,         \ jump back if no carry flag set
             ENDIF,
             TOS POP,
             NEXT,
             END-CODE

 

Edited by TheBF
bugs bugs bugs
  • Like 1
Link to comment
Share on other sites

I had always wondered if I could make a progress bar for the TI-99. Turns out it was a little more code than I expected but this works.

I couldn't think of a simple way to compute the values of left to right vertical lines in a character so I make a lookup table.

The algorithm uses 2 characters. One character is a solid block and the second is invisible.

With a numeric input we use /MOD to compute the number of complete blocks to draw and we use the modulo result to select the correct number of vertical lines to add to the invisible character. 

 

To use it you put word PROGRESS in the loop with the x,y position and it will update. You erase it with NEWBAR.

This version has no way to calibrate to a specific width but that would be a good enhancement.  I could envision it doing a wrap when a limit was encountered.

 

It is not too big if you don't try to overgeneralize it (like you would in OOP) to use in a TI-99 application.

 

And... I found another use for my new primitive 1+@. :)

 

EDIT:  Updated with final code that restores cursor position after progress bar is moved.

         *Demo changed to "TESTS".  Takes input argument for defined number of tests

 

\ Progress BAR for Camel99 Forth  bfox Jan 5, 2020

NEEDS HCHAR  FROM DSK1.GRAFIX

HEX
FFFF FFFF FFFF FFFF PATTERN: ABLOCK
 ABLOCK 0 CHARDEF  \ char 0 is the block

: FILLCHAR ( HEXpat ascii -- ) ]PDT  8 ROT VFILL ;
: GET-XY   ( -- x y ) VROW 2@ ;

\ define vertical line pattern from left to right
CREATE LNVALUES 00 C, 80 C, C0 C, E0 C, F0 C, F8 C, FC C, FE C,

\ input value fills  Character 1 with correct pattern
: HFINE ( 0..7 -- 'c ) LNVALUES + C@ 1 FILLCHAR  ;

: HGRAPH ( n  -- )
         8 /MOD ?DUP
         IF   DUP VPOS SWAP 0 VFILL  VCOL +!
         THEN HFINE 1 EMIT ;

\ ===[ User API words ]===
VARIABLE BARY
: PROGRESS ( x y -- ) GET-XY 2SWAP  AT-XY  BARY 1+@ HGRAPH  AT-XY ;
: NEWBAR   ( x y -- ) GET-XY 2SWAP  CLRLN BARY OFF AT-XY ;
: BARCOLOR ( n -- ) 0 SWAP 1 COLOR ;

\ DEMO
DECIMAL
: TESTS ( n -- )
       7 BARCOLOR
       0 ?DO
          0 0 NEWBAR
          237 0
          DO
           0 0 PROGRESS
           25 MS
          LOOP
       LOOP ;

Edited by TheBF
Final code
  • Like 2
Link to comment
Share on other sites

Two speedups for the VDP Driver

 

After I changed WITHIN to use the stack items in memory rather than popping them into registers I wondered if there were any other places I could do that.

Turns out my VC! (VSBW in TI speak) could be improved significantly:

\ VSBW Forth style  pronounced "Vee-Cee-store"
CODE: VC!  ( char Vaddr  -- )  
             TOS  R0 MOV,
             WMODE @@ BL,
            *SP SWPB,
            *SP+ VDPWD @@ MOV,    \ write char & POP
             TOS POP,             \ refill TOS
             NEXT,
             END-CODE

I may have committed a serious faux pas here but note the use of the MOV instruction to write a byte to the VDP write data port.

This allows me to write the data and POP the Forth Data stack at the same time. It seems to work.   Let me know why I can't/shouldn't do it.

I don't think there is anything for the extra byte to write to but I have been wrong many times. :)

 

The second speedup is regarding trying to speed VREAD/VWRITE (VMBR/VMBW) by writing 2 bytes in the inner loop.

I tried changing both which resulted in a resounding CRASH.

However when I only changed VREAD it worked.  This is probably because all my strings are aligned to even addresses when they are created.


\ VMBR Forth style
CODE: VREAD   ( VDP-adr RAM-addr cnt  -- )
              R1 POP,              \ pop buffer addr. to R1
              R0 POP,              \ pop VDP addr. to R0
              R3 VDPRD LI,         \ put the VDP read port address in R3. 12.9% faster
              RMODE @@ BL,         \ call the RMODE control routine
              TOS INCT,
              TOS -2 ANDI,         \ align to even address boundary
              BEGIN,
                *R3 *R1+ MOVB,     \ READ char from VDP RAM into CPU RAM , autoINC
                *R3 *R1+ MOVB,     \ READ 2nd char
                 TOS DECT,         \ dec the counter by 2
              EQ UNTIL,            \ jump back if carry flag not set
              TOS POP,             \ refill TOS
              NEXT,
              END-CODE

The bottom line is that with these two changes the SEVENS7 problem test speeds up by 4.5% because of faster scrolling and text typing.

That's a pretty good improvement.

 

 

Edited by TheBF
typo
  • Like 1
Link to comment
Share on other sites

3 hours ago, TheBF said:

I may have committed a serious faux pas here but note the use of the MOV instruction to write a byte to the VDP write data port.

This allows me to write the data and POP the Forth Data stack at the same time. It seems to work.   Let me know why I can't/shouldn't do it.

I don't think there is anything for the extra byte to write to but I have been wrong many times. :)

 

All of the VDP windows start on even addresses and, because only 1 byte is passed through each of those windows, it would appear safe to do what you did. ?

 

3 hours ago, TheBF said:

The second speedup is regarding trying to speed VREAD/VWRITE (VMBR/VMBW) by writing 2 bytes in the inner loop.

I tried changing both which resulted in a resounding CRASH.

However when I only changed VREAD it worked.  This is probably because all my strings are aligned to even addresses when they are created.

 

The write to VRAM probably takes longer to recover than the read, although I thought @Tursi had determined that, in practice, we could not outrun the VDP. If that is, indeed, what is happening, why not try a NOP, (see edit below) between the writes to see whether that is faster than a single-write loop?

 

[EDIT:  I do not know how you would implement NOP, in Camel99 Forth. It is not defined in fbForth, so it would need to be replaced with “ 0 JMP, ” there.]

 

...lee

Edited by Lee Stewart
correction of NOP,
Link to comment
Share on other sites

4 hours ago, Lee Stewart said:

 

All of the VDP windows start on even addresses and, because only 1 byte is passed through each of those windows, it would appear safe to do what you did. ?

 

 

The write to VRAM probably takes longer to recover than the read, although I thought @Tursi had determined that, in practice, we could not outrun the VDP. If that is, indeed, what is happening, why not try a NOP, (see edit below) between the writes to see whether that is faster than a single-write loop?

 

[EDIT:  I do not know how you would implement NOP, in Camel99 Forth. It is not defined in fbForth, so it would need to be replaced with “ 0 JMP, ” there.]

 

...lee

That's an interesting idea.  I have NOP in the Assembler which I took from TI-Forth as a starting place.

 

: NOP, ( -- ) 0 JMP, ;

 

I will give it a try, but I think it's failing because I am writing to the wrong place with that extra byte in some cases. 

The first thing that fails is opening the DSK1.START file. So that is the PAB read/write process.

 

 

 

Link to comment
Share on other sites

35 minutes ago, TheBF said:

I have NOP in the Assembler which I took from TI-Forth as a starting place.

 

: NOP, ( -- ) 0 JMP, ;

 

I do not think it was in the original TI Forth Assembler or I would have it in fbForth’s. Someone must have added it to the copy from which you got it.

 

...lee

Link to comment
Share on other sites

18 minutes ago, Lee Stewart said:

 

I do not think it was in the original TI Forth Assembler or I would have it in fbForth’s. Someone must have added it to the copy from which you got it.

 

...lee

Lol.  Maybe it was me. :)

I might have found it in a book I bought in the 80s from TI about programming on the 990. It was only 35 years ago or so. 

I always loved how easy it was to make macros as colon definitions.

  • Like 2
Link to comment
Share on other sites

Found it.


 

"Software Development" 1981 Texas Instruments,  page 8-103


8.12.7   Psuedo-Instructions
Instruction             Format           Effect
-----------------------------------------------
No Operation             NOP              JMP $+2
Return                   RT                B   *R11
===========================================================

And to that list I have added these to the cross compiler Assembler:
: IP++         IP INCT, ;
: RP++         RP INCT, ;
: RP--         RP DECT, ;
: SP++         SP INCT, ;
: SP--         SP DECT, ;

\ PUSH & POP on both stacks
: PUSH,         ( src -- )  SP DECT,  *SP   MOV, ;  
: POP,          ( dst -- )  *SP+      SWAP  MOV, ; 

: RPUSH,        ( src -- ) RP DECT,  *RP   MOV,  ;
: RPOP,         ( dst -- ) *RP+      SWAP  MOV,  ;



 

Link to comment
Share on other sites

VDP read/write one more time.

 

The code below seems to work with double byte VDP read/write and half the looping.

I was concerned about weird effects with strings so I compiled my experimental version of Oregon Trail. It is 995 lines long and puts 4K of text into VDP RAM.

I seems to work perfectly.

 

And with these three changes (VC!, VREAD, VWRITE) the Sevens Problem runs in 1:08 versus  1:26 with the old code.

If these don't demonstrate bugs over the next while they look like real improvements.

More testing required.

 

\ VMBR Forth style
CODE: VREAD   ( VDP-adr RAM-addr cnt  -- )
              R1 POP,              \ pop buffer addr. to R1
              R0 POP,              \ pop VDP addr. to R0
              R3 VDPRD LI,         \ put the VDP read port address in R3. 12.9% faster
              RMODE @@ BL,         \ call the RMODE control routine
              TOS INCT,
              TOS -2 ANDI,         \ align to even address boundary
              BEGIN,
                *R3 *R1+ MOVB,     \ READ char from VDP RAM into CPU RAM , autoINC
                *R3 *R1+ MOVB,     \ READ 2nd char
                 TOS DECT,          \ dec the counter
              EQ UNTIL,            \ jump back if carry flag not set
              TOS POP,             \ refill TOS
              NEXT,
              END-CODE

\ ===================================================================
CR ." V D P   W R I T E"

\ VMBW in Forth style
CODE: VWRITE  ( RAM-addr VDP-addr cnt -- )
             R0 POP,              \ vaddr to R0
             R1 POP,              \ cpu addr to R1
             TOS INCT,
             TOS -2 ANDI,         \ align to even address boundary
             NE IF,               \ protect from 0 count
                 WMODE @@ BL,
                 R3 VDPWD LI,      \ vdp addr. in a reg. makes this 12.9% faster
                 BEGIN,
                   *R1+ *R3 MOVB,  \ write byte to vdp write port
                   *R1+ *R3 MOVB,  \ write byte to vdp write port
                    TOS DECT,       \ dec the byte counter
                 EQ UNTIL,         \ jump back if no carry flag set
             ENDIF,
             TOS POP,
             NEXT,
             END-CODE

 

 

OregonTrailScreen.jpg

 

SEVENS NEW VDP CODE.jpg

Edited by TheBF
changed 8K to 4K. (>2000->1000= 4K
  • Like 1
Link to comment
Share on other sites

3 hours ago, TheBF said:

Found it.


"Software Development" 1981 Texas Instruments,  page 8-103

8.12.7   Psuedo-Instructions
Instruction             Format           Effect
-----------------------------------------------
No Operation             NOP              JMP $+2
Return                   RT                B   *R11
===========================================================

 

 

That does not count. :)  That is pure Assembler, not Forth Assembler. I was just saying that if it had been coded in the TI Forth Assembler blocks, I would have put it in fbForth’s FBLOCKS. I just never needed it until my suggestion for a VDP delay. You probably did what you are very good at: created it when you needed it, wanted it or thought it would be a good idea. I so enjoy watching you create Forth code. I think I am much better at reworking or finding flaws in existing code than dreaming up original code.

 

...lee

Link to comment
Share on other sites

1 hour ago, Lee Stewart said:

 

That does not count. :)  That is pure Assembler, not Forth Assembler. I was just saying that if it had been coded in the TI Forth Assembler blocks, I would have put it in fbForth’s FBLOCKS. I just never needed it until my suggestion for a VDP delay. You probably did what you are very good at: created it when you needed it, wanted it or thought it would be a good idea. I so enjoy watching you create Forth code. I think I am much better at reworking or finding flaws in existing code than dreaming up original code.

 

...lee

You are totally right, I believe after seeing this information I added it to the Forth Assembler.  I had no idea why I would need it at the time I'm sure.

 

Well thank you. :)

I have always been a hybrid creature. In my youth I wanted to be a musician and did become a part-time professional musician. (it helped pay for school)

I spent some time in an advertising agency and a broadcasting facility engineering department where there were opportunities for creative projects because not everything we needed could be purchased off the shelf in those days. Once I learned Forth I "created" a few projects for myself.

I enjoy hanging with creative people. It pushes you.

Link to comment
Share on other sites

1 hour ago, Lee Stewart said:

 

So, what was wrong with your original VWRITE code?

 

...lee

I did not evaluate why it failed, I just tried a number of different ways to accomplish the same thing and this one worked so I migrated it back to the VREAD word.

We shall see if it's really going to fly.

Link to comment
Share on other sites

I think I have got to  the end of my improvements on my VDP driver.

The last weakness was my calculation of the VDP screen address derived from the cursor row and column.

 

So I used some of the last free bytes in the 8K image and added:

CODE: GET-XY  ( -- col row )
             TOS          PUSH,
             TOS          STWP,
             34 (TOS)     PUSH, \ vcol user variable
             32 (TOS) TOS MOV,  \ vrow user variable
             NEXT,
             END-CODE

 

GET-XY feeds into

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
             VPG @@  TOS ADD,   \ add Video page offset
             NEXT,
             END-CODE

Which allows the new faster VC! (VSBW) to put a character onto the correct video memory page like this

\ primitive to put char on screen at cursor position
: VPUT   ( char -- )  GET-XY >VPOS VC! ;

With all Assembly language words doing the job and the new faster scroll, which uses the faster VREAD and VWRITE, runs the Lee Stewart version of the Sevens problem in 1:10 (stop watched timing)  Versus an earlier kernel of  CAMEL99 that I thought was pretty fast which runs the same code in 1:26. That's almost a 23% improvement.

It could be made faster by making all the routines inline, but that exceeds the memory I have left and is of questionable value since I have a direct to VDP ram word VTYPE.

 

So far I have not seen any strange artifacts as a result of moving 2 bytes at a time in and out of VDP RAM.

 

 

Link to comment
Share on other sites

I was wrong :)

 

It was even better to do this. It took the time down to 1:06.8  And uses less word names so it even saved space. GET-XY is simple to define later if I really need it.

 

This way of interleaving code words only works for indirect thread code. The CFA! compiler directive takes a label and stores it in the 'code-field-address' of a word.

Later when Forth "interprets" this it jumps to the address of the label.

 

There are few superfluous instructions to make everything stack compatible, but it's not bad.

variable: VPG   \ holds address of VDP page ( >400 increments only)

CODE: VPUT  ( char -- )  \ put a char at cursor position
             TOS PUSH,
             R1         STWP,
             32 (R1) R3  MOV,    \ vrow->r1
             2E (R1) R3  MPY,    \ vrow*c/l->tos
             34 (R1) TOS ADD,    \ add vcol
             VPG @@  TOS ADD,    \ add video page
l: _VC!  ( char Vaddr  -- )      \ VSBW in Forth style  pronounced "Vee-Cee-store"
             TOS  R0 MOV,
             WMODE @@ BL,
            *SP SWPB,
            *SP+ VDPWD @@ MOV,    \ write char & POP
             TOS POP,             \ refill TOS
             NEXT,
             END-CODE

CODE: VC!    _VC! CFA!   END-CODE

 

  • Like 1
Link to comment
Share on other sites

BENCHIE Re-visited

With V2.58 running I pulled out this old chestnut. This is the original BENCHIE that uses VALUES.  In the past I had a glaring error in my implementation of the Forth word 'TO' that assigns a number to a VALUE.  I was computing the address AND I was adding the 2 byte offset at runtime.  Pretty stupid.  The new DSK1.VALUES library file does that calculation at compile time and then compiles the computed address as a literal number into your code. This makes BENCHIE much faster. From 1:18 to 1:01.

For reference Turbo Forth does this BENCHIE in 55 seconds.  (stopwatch timing for all)  So CAMEL99 V2.58 is only 10% off of Turbo Forth which is pretty good considering how many intrinsics are in scratch-pad RAM with Turbo Forth.

 

Full disclosure Camel99 Forth has three primitive operations in 16 bit RAM,  '@'  'DROP'  and  'LIT'  as well as the inner interpreter routines (enter,exit,next) and the virtual machine branching routines, ?BRANCH and BRANCH.

 

Here is the new 'TO' in ANS Forth. It is a state smart word which is considered sinful in 2020 by the commercial Forth system writers but it works for me.

: VALUE   CONSTANT ;

: TO  ( n -- )
           STATE @
           IF   '  >BODY         \ compute PFA at compile time
                POSTPONE LITERAL  POSTPONE !
           ELSE '  >BODY !
           THEN ; IMMEDIATE

Here is BENCHIE as tested.

\ benchie 

\ 8051 ANS Forth (12 MHz 80C535): 15.8 sec (with user variables)
\ CAMEL99    V2.0      1:18
\            V2.58     1:01
\ TurboForth V1.2.1    0:55

INCLUDE DSK1.VALUES ( for CAMEL99 only)

HEX  
  5 CONSTANT FIVE
100 CONSTANT MASK

  0 VALUE BVAR

: BENCHIE
         256 0 
         DO
            1
            BEGIN
              DUP SWAP DUP ROT DROP 1 AND
              IF FIVE +
              ELSE 1-
              THEN TO BVAR
              BVAR DUP MASK AND
            UNTIL
            DROP
         LOOP ;

 

 

  • Like 1
Link to comment
Share on other sites

Remember when we were all envious of people who had a C64? :)

 

I went looking for Forth benchmark programs and found this site:

https://theultimatebenchmark.org/

 

I tried this one that exercises integer calculations:

DECIMAL
32000 CONSTANT INTMAX

VARIABLE INTRESULT

: DOINT
  1 DUP INTRESULT DUP >R !
  BEGIN
    DUP INTMAX <
  WHILE
    DUP NEGATE R@ +! 1+
    DUP R@ +! 1+
    R@ @ OVER * R@ ! 1+
    R@ @ OVER / R@ ! 1+
  REPEAT
  R> DROP DROP
;

On CAMEL99 Forth it ran in 14.8 seconds.

The reported time for an equivalent Forth system is 526 seconds. !!!

And on a sub-routine threaded C64 system which should be about (more or less) the same speed as Small C, it took 37 seconds.

 

The old girl looks pretty good.

 

 

  • Like 2
Link to comment
Share on other sites

5 hours ago, TheBF said:

Here is the new 'TO' in ANS Forth. It is a state smart word which is considered sinful in 2020 by the commercial Forth system writers but it works for me.


: VALUE   CONSTANT ;

: TO  ( n -- )
           STATE @
           IF   '  >BODY         \ compute PFA at compile time
                POSTPONE LITERAL  POSTPONE !
           ELSE '  >BODY !
           THEN ; IMMEDIATE

 

 

Why not this?:

: TO  ( n -- )
           '  >BODY         \ compute PFA at compile/run time
           STATE @
           IF   
                POSTPONE LITERAL  POSTPONE !   \ defer to runtime
           ELSE !
           THEN ; IMMEDIATE

 

...lee

  • Like 1
Link to comment
Share on other sites

27 minutes ago, Lee Stewart said:

 

Why not this?:


: TO  ( n -- )
           '  >BODY         \ compute PFA at compile/run time
           STATE @
           IF   
                POSTPONE LITERAL  POSTPONE !   \ defer to runtime
           ELSE !
           THEN ; IMMEDIATE

 

...lee

Umm... because... well  ermmm...   :)

 

Nice one.

 

  • Like 1
  • Haha 1
Link to comment
Share on other sites

First VWRITE BUG

After a number of distractions I got back to looking at my ED99 editor.  I encountered a bug when I tried to write a pattern to character >1F, an ODD number.

I am trying a fix that is a bit of brute force, but it seems to work.

I test the byte count to see if it is ODD. If so I run a single byte loop, otherwise I run a double byte loop.

It solved the problem but it added a few bytes.

We shall see if everything behaves.

 

CODE: VWRITE  ( RAM-addr VDP-addr cnt -- )
             R0 POP,                \ vaddr to R0
             R1 POP,                \ cpu addr to R1
             TOS TOS MOV,           \ 0 byte-count protection
             NE IF,
                WMODE @@ BL,
                R3 VDPWD LI,        \ vdp addr. in a reg. makes this 12.9% faster
                TOS W MOV,
                W  0001 ANDI,       \ test for odd byte count
                NE IF,
            \ ODD BYTE COUNT
                   BEGIN,
                    *R1+ *R3 MOVB,  \ write byte to vdp write port
                     TOS DEC,       \ dec the byte counter
                    EQ UNTIL,       \ jump back if no carry flag set
                ELSE,
             \ EVEN BYTE COUNT
                   BEGIN,
                    *R1+ *R3 MOVB,  \ write byte to vdp write port
                    *R1+ *R3 MOVB,  \ write byte to vdp write port
                     TOS DECT,      \ dec the byte counter
                   EQ UNTIL,        \ jump back if no carry flag set
                ENDIF,
             ENDIF,
             TOS POP,
             NEXT,
             END-CODE

 

Link to comment
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.
Note: Your post will require moderator approval before it will be visible.

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...