Jump to content
IGNORED

Camel99 Forth Information goes here


TheBF

Recommended Posts

Phwoar! This is really cool. I never got a chance to look at serial. Do you mind if I use this this code (with attribution, of course) for TurboForth?

 

Would you mind posting some example outputs from BAUD (i.e the value that gets written into BPS) for some example baud rates? I need to check UM/MOD implementation in TF :-)

 

Also, can you post your code for MS if you don't mind? :-)

 

It'll give me something to look at over Christmas (along with Lee's recent work of CF7 and variants) now that uni has stopped until January.

 

Thanks

 

Mark

 

Of course you can use it, as long as I can re-used the fixes you find. :-)

 

Actually I am working on it on real iron and there is a bug with the baud rate setting for lower rates. 9600 and 19200 worked ok.

I found a table in an old TI book and it looked linear down to 600 Baud.

 

I am not sure if I can get handshaking correct for the receive side. I don't have an rs232 breakout box so it's painful.

Without interrupts of course you drop characters easily but for a console port TTYKEY is fast enough for human fingers.

A few machine code words would make it quicker.

 

The MS code is here in my cross-compiler Forth.

 

https://github.com/bfox9900/CAMEL99-V2/blob/master/SRC/CCLIB/TICKTOCK.HSF

 

My little ticker is strange because it has to accommodate

1. My compiler can only build 8K images

2. I need to support cooperative multitasking

 

But it seems to work well on real hardware giving a consistent time. I have a constant in the code that compensates for Forth overhead and it might be better to be a variable that could be calibrated to a machine.

It could be much finer grained as a CODE word that stops the system, or I suppose it could trigger an interrupt.

Lots of ways to make it better.

The TMR! word starts the 9901 running continuously which broke Classic99 but Tursi fixed it for me. (Thanks Tursi)

 

Merry Christmas! :-)

Link to comment
Share on other sites

Would you mind posting some example outputs from BAUD (i.e the value that gets written into BPS) for some example baud rates? I need to check UM/MOD implementation in TF :-)

 

 

These numbers concur with "Software Development, Geoff Vincent, Jim Gill, TI Oct 1981.

post-50750-0-73650300-1544828498.jpg

Link to comment
Share on other sites

 

These numbers concur with "Software Development, Geoff Vincent, Jim Gill, TI Oct 1981.

 

 

I see you're rounding down, in favor of going faster. Correct?

 

I looked at source code and found that TE3 had >1A1 for 1200 baud - rounding 1E6/1200/2 = 416.7 = 1A1 = 1199.04 bps.

At some point I corrected this to 1A0 for TI-Net BBS which is 1201.92 bps.

 

Link to comment
Share on other sites

  • 3 weeks later...

HAPPY NEW YEAR 99ERS

 

The guests have not arrived yet so I thought I would give my best wishes to all the people here for 2019.

 

It has a been good year for CAMEL99 Forth. I have learned a lot about the old system that I didn't know 35 years ago.

Most of that due to the extraordinary talent of the people who frequent this forum.

 

Based on my recent discussions with Lee I have added BLOCK files to the system.

It was pretty simple to add them as a layer on top of the ANS Forth file word-set but I still had a round of bug killing during the Christmas holidays.

 

For the curious here is the code to give CAMEL99 Forth style virtual memory blocks.

\ blocks.fth  for CAMEL99 Forth     Dec 17 2018 BJFox
\ Based on ideas from HsForth by Jim Kalihan (RIP)

NEEDS .S         FROM DSK1.TOOLS
NEEDS OPEN-FILE  FROM DSK1.ANSFILES
NEEDS .R         FROM DSK1.UDOTR

HEX
2                CONSTANT #BUFF     \ # of active buffers
400              CONSTANT B/BUF
B/BUF 2 CELLS +  CONSTANT B/REC     \ block-record has a 4 byte header
7FFF             CONSTANT $7FFF

3FFF 1-          CONSTANT LIMIT      \ end of buffer memory
LIMIT B/REC #BUFF * - CONSTANT FIRST  \ first buffer record address

DECIMAL 128      CONSTANT B/SEC     \ bytes per sector on TI disk

VARIABLE BLK
VARIABLE PREV      FIRST  PREV !
VARIABLE USE       FIRST  USE  !
VARIABLE LOWBLK
VARIABLE HIGHBLK   79  HIGHBLK !    \ set the highest block
VARIABLE BHNDL                      \ block file handle

HEX
 : CELL-   S" 2- " EVALUATE ; IMMEDIATE

DECIMAL
CREATE ACTIVE  20 ALLOT    \ block file name
: ACTIVE$!  ( f$ len - f$) ACTIVE PLACE ;
: ACTIVE$   ( -- addr len) ACTIVE COUNT ;

: ERASE     ( addr len -- )  0 FILL ;
: BLANKS    ( addr len -- ) BL FILL ;

\ ===================================================
\ interface to ANS File system
HEX
: ?BLOCKS   ( -- )  BHNDL @ 0= ABORT" No open BLOCK file" ;

\ move file pointer to start of block
: SEEK   ( blk# -- )
         ?BLOCKS
         DUP BLK !
         8*   ( blk# x 8 = sector)
         BHNDL @ REPOSITION-FILE ABORT" SEEK err" ;

\ READ/WRITE TI records for 1 block
: RBLK  ( adr blk# -- adr)
          SEEK
          DUP B/BUF BOUNDS ( end-addr,start-addr)
          DO    
            I B/SEC BHNDL @ READ-LINE ?FILERR  2DROP
          B/SEC +LOOP ;

: WBLK  ( adr blk# -- )
          SEEK
          B/BUF BOUNDS ( end-addr,start-addr)
          DO   
             I B/SEC BHNDL @ WRITE-LINE ?FILERR
          B/SEC +LOOP ;

\ ===================================================
HEX
: UPDATE ( -- ) PREV @ @ 8000 OR  PREV @ ! ;

: +BUF    ( addr1-- addr2)
          B/REC + DUP LIMIT = IF DROP FIRST THEN ;

: BUFFER ( n -- addr )
        USE @ DUP >R       \ get current buffer record & Rpush
        @ 0<               \ has it been updated?
        IF                 \ if true ...
           R@ CELL+        \ get buffer address
           R@ @            \ get the block number
           $7FFF AND  WBLK  \ write data to disk
        THEN R@ !          \ store this in USE record
        R@ PREV !          \ set it as previous record
        R@ +BUF USE !      \ advance to next buffer, make the USE
        R> CELL+ ;         \ return the buffer address

: BLOCK   ( block# --- addr )
        ?BLOCKS
       >R
        PREV @ DUP @  R@ - $7FFF AND
        IF
           BEGIN
              +BUF DUP PREV @ =
              IF
                 DROP R@ BUFFER  R@ RBLK CELL-
              THEN
              DUP @ R@ -  $7FFF AND
           0= UNTIL
           DUP PREV !
           DUP USE @ =
           IF
              DUP +BUF USE !
           THEN
      THEN
      R> DROP CELL+ ;

HEX
: FLUSH ( -- )
        ?BLOCKS
        FIRST           \ start at 1st block record
        #BUFF 0
        DO
           DUP @ 0<    \ is block updated?
           IF          \ yes, write to disk
              DUP @ $7FFF AND OVER 2DUP !
              CELL+ SWAP WBLK
           THEN +BUF   \ then goto next block record
        LOOP
        DROP ;

: EMPTY-BUFFERS ( -- )
        FIRST LIMIT OVER - ERASE
        #BUFF 0
        DO
          $7FFF B/REC I * FIRST + !
        LOOP ;

DECIMAL
: DF128   DISPLAY RELATIVE  B/SEC FIXED ;

:  OPEN-BLOCKS ( file$ len -- )
        2DUP ACTIVE$!
        EMPTY-BUFFERS
        DF128 R/W OPEN-FILE ?FILERR BHNDL ! ;

HEX
: CLOSE-BLOCKS ( -- )
        BHNDL @ ?DUP
        IF CLOSE-FILE ?FILERR
           BHNDL OFF
        THEN ;

\ Usage:  45 S" DSK1.MYBLOCKS" MAKE-BLOCKS
:  MAKE-BLOCKS ( n file len -- )
        OPEN-BLOCKS
        FIRST CELL+ B/BUF BLANKS
        DUP HIGHBLK !
        1+  1
        DO
           FIRST CELL+ I WBLK
        LOOP
        CLOSE-BLOCKS ;
  • Like 3
Link to comment
Share on other sites

PAGED MEMORY OPERATIONS

 

I have scratching my head on how to use the SAMS memory to implement an editor that can handle large files (64K)

I have something that works. It's not blazing fast on writing individual bytes, but it will handle typing speeds.

For block string operations to paged memory one will have to always prevent reading or writing across page boundaries but that's not a big hardship.

 

The design criteria were as follows:

  1. simplify the computation by standardizing on one 4K page in low memory at >3000. (no need to compute the register address)
  2. Only map in a new page when needed (this improved speed by 50%)
  3. Limit direct access to 64K range because that's the simplest to index
  4. use a segment variable to allow selecting other 64K segments
  5. The PAGED word will be combined with standard fetch and store operators to create the final APi

I did it first completely in Forth: (it was a little slow but it worked)

The UM/MOD operation takes an address and a segment number as a 32bit integer and divides it by 4K to give an offset into the page and the bank#

 : PAGED  ( addr -- addr')
         SEG @ 4K UM/MOD  ( -- offset bank#)
         DUP BANK# @ =            \ are we using the same PAGE
         IF
             DROP                 \ Yes! Drop bank# and get out
         ELSE
             DUP FF00 AND ABORT" SAMS Err!"
             DUP BANK# !           \ update bank# variable
             ><                    \ swap bytes, bank# must be in left byte
            1E00 CRU! 0SBO         \ enable SAMS card
           ( bank#) 4006 !         \ store bank in 3K SAMS register
             0SBZ                  \ disable SAMS card         

        THEN  PMEM +               \ then add offset to paged mem block
;
\ paged memory fetch and store
: C@P    ( addr -- n)    PAGED C@ ;  \ fetch a byte
: C!P    ( n addr -- ) PAGED C! ;    \ store a byte

: @P     ( addr -- n)  PAGED @ ;    \ fetch an int
: !P     ( n addr -- ) PAGED ! ;    \ store an int

Then I replaced the conditional part with a CODE word and this sped things up by 40% or so.

CODE ?MAP ( offset bank# -- )
          TOS BANK# @@ CMP,
          EQ IF,
                 TOS POP,             \ no need to switch

          ELSE, ( *THE MAPPER* )
                TOS BANK# @@ MOV,     \ record the NEW bank#
                         TOS SWPB,    \ bank# needs to be in left byte
                    R12 1E00 LI,      \ cru address of SAMS CARD
                           0 SBO,     \ enable SAMS card
                 TOS 4006 @@ MOV,     \ load the >3000 sams register
                           0 SBZ,     \ disable sams card
                         TOS POP,     \ drop the bank#
          ENDIF,
          TOS PMEM AI,                \ add offset to paged mem block
          NEXT,
          ENDCODE

: PAGED ( addr -- addr') SEG @ 4K UM/MOD ?MAP ;

Further speed improvements need to remove the divide operation with another code word.

Doing it in Forth was the same speed as using UM/MOD.

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

PAGED Memory performance in Forth vs CPU RAM Operations

Threaded Forth imposes a 3 to 4 times speed penalty on primitive operations when compared to native code instructions.
Adding paged memory to writing one byte, the worst case, seems to incur another 3 times speed penalty using the code in the previous post. (with ?MAP)
Block memory writes however can run at processor speed and in the best case, filling one entire 4K page, the penalty of switching banks disappears.
(Note: FILL , used in BLANKS, is written in Forth Assembler)

Results are below:


\ testing read write speeds to SAMS memory
NEEDS DUMP   FROM DSK1.TOOLS
NEEDS ELAPSE FROM DSK1.ELAPSE

HEX 
7FFF CONSTANT 32K
FFFF CONSTANT 64K
1000 CONSTANT 4K

: ERASE    0 FILL ;
: BLANKS  BL FILL ;

\ 64k single byte writes to paged memory
: 64KBYTES    64K 0 DO  I     I C!P    LOOP ; ( 46 secs)

\ 64K single byte writes to single address
: 64KBTEST    64K 0 DO  I  3000 C!    LOOP ; ( 14.9 secs)

\ 32K word writes to paged memory
: 32KWORDS    64K 0 DO  I  I    !P   2 +LOOP ; ( 25.5 secs)

\ 32K word writes to single address
: 32KTEST     64K 0 DO  I  3000  !    2 +LOOP ; ( 9.6 secs)

\ 4K block fill to paged memory 
: 64KBLANKS 64K 0 DO I PAGED 4K BLANKS 4K +LOOP ; ( 1.5 secs) 

\ 4K block fill to CPU memory 
: 64KTEST 64K 0 DO 3000 4K BLANKS 4K +LOOP ; ( 1.5 secs)

EDIT: Be careful what you wish for ;-)

 

All the memory is great but out of curiosity I wondered how long it takes for Forth to erase fifteen 64K segments (983,040 bytes)

: 64KERASE    64K 0 DO I PAGED 4K ERASE   4K +LOOP ;

: ERASEALL    10 1 DO  I SEG !  64KERASE   LOOP ;  ( 20.7 secs)

Wow!

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

After trying SAMS code on real iron and failing, I went back to the Micropendium article by Bruce Harrison whereupon I noticed I did not write an Initialization routine. Duh!

 

In Forth that looked like this:

\ * SAMSINI sets card to "power-up" condition
: SAMSINI
       1E00 CRU! 0SBO    \ turn on card
       0                 \ 1st value
       4000 20           \ register address, #regs
       BOUNDS
       DO
           DUP I !       \ I is reg. address
           0101 +        \ next value
       2 +LOOP
       0SBZ              \ turn off card
       DROP
;

Note:

Something that I have noticed is that the semantic power of 9900 assembler is actually about the same as Forth.

What this means is that for primitive (low level) routines, Assembler code is smaller than Forth "most" of the time. This is not true for less powerful CPUs.

I have to get a native code Forth compiler running.

Here is Bruce's AMSINI routine in Forth Assembler: ( I simplified it slightly by starting with 0 in R1 and changing the instruction order in the loop)

CODE SAMSINI
       R12 1E00 LI, 0 SBO, \ turn on Sams card
       R1 CLR,
       R0 4000 LI,        \ start of memory
       BEGIN,
          R1 R0 *+ MOV,   \ move 2 bytes to mem-mapper
          R1 0101  AI,    \ add 1 page
          R0 4020 CI,     \ all done?
       EQ UNTIL,          \ no, init more
       0 SBZ,             \ turn off SAMS card
       NEXT,              \ return
       ENDCODE

I took the step of creating Bruce's article as a PDF file and adding the Forth code and example for anyone who is interested.

 

 

SAMS CARD ACCESS and MAPPING.pdf

  • Like 4
Link to comment
Share on other sites

You're probably too far down the track now, but some of the SAMS code in TF may be help/interest.

Check out http://turboforth.net/source/Bank1/1-04-Memory.html (the word >MAP i.e "to mapper")

; ; >MAP ( bank address -- )
; If a SAMS card is present, maps memory bank "bank" to address "address"
_sams   mov r12,r11                 ; save address of NEXT
        mov *stack+,r1              ; get address
        andi r1,>f000               ; set to 4k boundary
        srl r1,11                   ; divide by 2048
        ai r1,>4000                 ; convert to SAMS register address
        mov *stack+,r2              ; get bank
        andi r2,>ff                 ; mask off any crap
        mov r2,r0                   ; keep a copy
        sla r2,8                    ; move to high byte
        xor r0,r2                   ; combine r0 & r2. Hi & lo bytes are now identical
        li r12,>1e00                ; cru address of SAMS
        sbo 0                       ; enable SAMS registers
        mov r2,*r1                  ; poke sams register
        sbz 0                       ; disable sams registers
        mov r11,r12                 ; restore address of NEXT
        b @retB0                    ; return to caller
Also, the SAMS initialisation code in http://turboforth.net/source/Bank1/1-16-Initialise.html
; initialise SAMS card if fitted
        li r12,>1e00                ; sams CRU base
        sbo 0                       ; enable access to mapper registers
        sbz 1                       ; disable mapping while we set it up
        li r0,>4004                 ; register for >2000
        li r1,>f8f8                 ; map bank >f8 into >2000
        mov r1,*r0+                 ; do it
        li r1,>f9f9                 ; map bank >f9...
        mov r1,*r0+                 ; ...into >3000
    ; now set up the banks for high memory...
        li r0,>4014                 ; register address
        li r1,>fafa                 ; register value
        li r2,6                     ; loop count
sams    mov r1,*r0+                 ; write to the register
        ai r1,>0101                 ; next register value
        dec r2                      ; finished?
        jne sams                    ; loop if not
        sbo 1                       ; enable mapping
        sbz 0                       ; lock the mapper registers

 

  • Like 2
Link to comment
Share on other sites

Thanks Mark,

 

I looked at your "bible" when I first started trying to figure this out. :-) It is always invaluable and such tidy code.

I have tested my Forth version which uses a single page at >3000 and it seems to work OK. I also opted to put my block buffers there since I use >2000 upwards as a tiny heap stack for screen scrolls and temp strings.

 

I did up the assembler version and the subbed in the machine code so I don't need the assembler when running on real iron.

It is about 60..70% faster than the Forth version when accessing byte or word at a time. I tried re-coding the DIV operation as binary masks etc.

but the number of instructions that it took meant it was over 140 cycles versus 204 or so for the DIV version, so I just optimized the DIV version. :-)

 

The code version looks like this now.

\ SAMS CARD support. 64K segmented memory fetch and store

\ NEEDS DUMP  FROM DSK1.TOOLS  \ debugging only
HERE

HEX
     VARIABLE BANK#      \ current mapped bank
1000 CONSTANT 4K         \ bytes per bank = 4K
3000 CONSTANT PMEM       \ paged memory block location
     VARIABLE SEG        \ holds current 64K segment

\ safely set the 64K segment that you want to use
: SEGMENT ( 1..F -- ) \ don't allow segment 0
          DUP 01 10 WITHIN 0= ABORT" BAD segment selected"
          SEG ! ;  
1 SEGMENT
\ using machine code so we don't need the CRU library
CODE SAMS-OFF  ( --)  \ disable mapped memory
          020C , 1E00 , \ R12 1E00 LI,
          1E01 ,        \ 1 SBZ,
          NEXT,
          ENDCODE

CODE SAMS-ON ( -- )   \ enable mapped memory
          020C , 1E00 , \ R12 1E00 LI,
          1D01 ,        \ 1 SBO,
          NEXT,
          ENDCODE

\ * AMSINI sets ams card to "power-up" condition
CODE SAMSINI
       020C , 1E00 , \ R12 1E00 LI,
       1D00 ,        \ 0 SBO,       ( turn on Sams card )
       04C1 ,        \ R1 CLR,
       0200 , 4000 , \ R0 4000 LI,  ( start of memory)
                     \ BEGIN,
       CC01 ,        \ R1 R0 *+ MOV, ( move to mem-mapper)
       0221 , 0101 , \ R1 0101  AI, ( add 1 page)
       0280 , 4020 , \ R0 4020 CI,  ( all done? )
       16FA ,        \ EQ UNTIL,    ( no, init more)
       1E00 ,        \ 0 SBZ,       ( turn off SAMS card)
       NEXT,         \ return
       ENDCODE

CODE >BANK  ( addr -- offset bank# )
          0200 , 4K ,    \ R0  4K LI,      \ 4K divisor ->R0     14
          C144 ,         \ TOS R5 MOV,     \ address to r5       18
          C120 , SEG ,   \ SEG @@ TOS MOV, \ segment to TOS      22
          3D00 ,         \ R0 TOS DIV,     \ unsigned division  124
          0646 , C585 ,  \ R5 PUSH,        \                     28
          NEXT,          \ 16 BYTES                             204
          ENDCODE

CODE ?MAP ( offset bank# -- )
          8804 , CD90 ,  \ TOS BANK# @@ CMP,
          1602 ,         \ EQ IF,
          C136 ,         \     TOS POP,
          100A ,         \ ELSE, ( *THE MAPPER* )
          C804 , CD90 ,  \    TOS BANK# @@ MOV,
          06C4 ,         \    TOS SWPB,
          020C , 1E00 ,  \    R12 1E00 LI,
          1D00 ,         \    0 SBO,
          C804 , 4006 ,  \    TOS 4006 @@ MOV,
          1E00 ,         \    0 SBZ,
          C136 ,         \    TOS POP,
                         \ ENDIF,
          0224 , PMEM ,  \ TOS PMEM AI,
          NEXT,
          ENDCODE

 : PAGED  ( addr -- addr') >BANK ?MAP ;

\ paged memory fetch and store
: C@P    ( addr -- n)    PAGED C@ ;   \ fetch a byte
: C!P    ( n 32addr -- ) PAGED C! ;   \ store a byte
: @P     ( 32addr -- n)  PAGED @ ;    \ fetch an int
: !P     ( n 32addr -- ) PAGED ! ;    \ store an int

SAMSINI

CR HERE SWAP - DECIMAL . .( bytes) HEX

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

I was going to show you how I did SAMS for fbForth 2.0 when I had a chance to grab a few minutes, but I am happy to see that @Willsy beat me to it, seeing as how I got the code from him in the first place! :P

 

...lee

 

We are a supportive lot aren't we?

(When I looked at the BLOCK code in HsForth I could see that he "reviewed" Fig-Forth or perhaps MVP Forth and changed it up a little so getting external inspiration is a noble tradition) :)

 

My objective was a lot simpler than Willsy's code. I just wanted one window into the SAMS memory rather than the general solution of allowing mapping of anything to anywhere.

 

Topic Shift

While reviewing my own sacred texts from the '80s I found this little "line editor" that I had put in a block for times when I just wanted to make a fast change.

I added to it here to make it a little more functional and so in 446 bytes I got a little editor that works surprisingly well.

It can PUT a line in a block, Delete a line, Copy a line and Move a line. It can advance to the next block or previous block.

It was also very simple to deal with the 40 column screen and allow seeing the Right or Left side of the block using the R and L commands

Combined with direct to VDP RAM writes it's also pretty fast.

 

And it salvages "EVALUATE" to do a line by line LOAD. The video shows it in action. It am shocked how useful it is albeit not fancy.

 

VARIABLE SCR
VARIABLE SOL
VARIABLE SWID   C/L@ 4 - SWID !

DECIMAL
64 CONSTANT LWIDTH

: (LINE) ( lin# -- addr) SCR @ BLOCK SWAP  LWIDTH * + ;
\ : .LINE  ( lin# --) (LINE) SOL @ + SWID @ TYPE ; ( slow version)
: .LINE  ( lin# - ) (LINE) SOL @ + VPOS  SWID @ VWRITE ;
: (CP) ( L1 L2 -- ) (LINE) SWAP (LINE) SWAP LWIDTH CMOVE ;

\ Line editor commands
: LIST   ( s# - ) PAGE DUP SCR ! ." SCR# "  3 U.R
         16 0 DO   CR I 2 .R  I .LINE   LOOP  CR QUIT ;

: ED ( -- ) SCR @ LIST ;
: >> ( -- ) 1 SCR +! ED ;
: << ( -- ) SCR @ 1- 0 MAX  SCR ! ED ;

: P  ( line# -- ) 0 PARSE ROT (LINE) SWAP CMOVE UPDATE  ED ;
: D  ( line# -- ) (LINE) LWIDTH BLANKS UPDATE ED ;
: CP ( L1 L2 -- ) (CP) UPDATE ED ;
: M  ( L1 L2 -- ) OVER >R (CP) R> D ;
: R  ( -- ) 28 SOL !  ED ;   \ list right side of block
: L  ( -- ) SOL OFF   ED ;   \ list left side of block

: LOAD  ( n -- )
        SCR !
        16 0  ( edit: EVALUATE changes SOURCE-ID)
        DO
           I LINES !
           I (LINE) LWIDTH EVALUATE
        LOOP ;

: -->   ( n -- ) SCR @ 1+ LOAD ;

LINEDITOR.mp4

Edited by TheBF
Link to comment
Share on other sites

  • 2 weeks later...

A Simple Shell Progam

How amazing is CLASSIC99. I started exploring the "MAKE" menu option. This allows you to save the current running program as an E/A5 program that can span multiple 8K sections.
I had created a set of separate file utilities for my own use and finally integrated them into one file. I realized this might be useful to others.

But how do I distribute it to the users here? My own cross-compiler can only create programs of 8K or less in size.

CLASSIC99 to the Rescue
After compiling the new shell code into CAMEL99 Forth I saved it as an E/A5 program. I had to create a new startup word to re-init a few details to replace the default COLD routine that does the job in forth but that was pretty simple. CAMEL99 has a BOOT variable that holds the pointer of the first Forth word that will run on startup so that made it easy as well.

So if you need a simple set of commands to view a disk that you can call up from E/A Cartridge this may be useful when you are working on real hardware. It's kind of big because it contains the entire Forth kernel (8K) + the SHELL CODE.

The attached ZIP file has the two binary images needed to run the shell.

Known BUGS:
1. The disk usage number is wrong. I will find out why.
2. MORE only works for DV80 files

Future Enhancements"
1. Some kind of REGEX to allow viewing selective directories
2. Add a file HEX dump for any type of file.

3. Tell me what you need. I might be able to code it!

For the curious the spoiler has the source code. EDIT: Updated to Version 0.2

 


\ CAMEL99 Forth shell for disk file management

\ NEEDS DUMP       FROM DSK1.TOOLS
NEEDS OPEN-FILE  FROM DSK1.ANSFILES
NEEDS VALUE      FROM DSK1.VALUES
NEEDS CASE       FROM DSK1.CASE
NEEDS BUFFER:    FROM DSK1.BUFFER

HERE
\ simple spinner to show activity
VARIABLE S
CREATE SPNR  CHAR | C, CHAR / C, CHAR - C, CHAR \ C,
: SPNR[S] ( -- addr) SPNR S @ + ;
: S++     ( -- )  S @ 1+ 3 AND S ! ;
: SPIN    ( -- ) SPNR[S] C@ EMIT BS S++ ; \ BS is backspace

\ use for file handles as needed
HEX
0 VALUE #1   0 VALUE #2   0 VALUE #3

\ CR if near end of screen
DECIMAL
: ?CR     OUT @ 10 + C/L@ > IF CR THEN ;

HEX
\ print unsigned int, right justified
: U.R  ( u n --) >R 0 <# #S #> ( adr len) R> OVER - SPACES TYPE ;

\ string helpers

10 BUFFER: SRC$ 
10 BUFFER: DST$

 : LEN      ( $addr -- c ) C@ ; 
 : ARG$     ( -- addr len ) BL PARSE-WORD DUP ?FILE ;
 : $.       ( $addr -- ) COUNT TYPE ;
 : $.LEFT   ( $ width -- ) OVER LEN - >R $.  R> SPACES ;
 : NEXT$    ( addr len -- addr' len') + COUNT ;
: +PLACE      ( addr n $ -- ) 2DUP 2>R  COUNT +  SWAP CMOVE 2R> C+! ;

\ file path cutter
: /.        ( caddr len -- caddr' len' )  [CHAR] . SCAN ;
: DEV./     ( caddr len -- dev. len' )    2DUP /. NIP - 1+ ;
: /FILENAME ( caddr len -- filename len') /. 1 /STRING ;

: POS$  ( $1 $2 -- n )  \ return "position" $1 in $2
           TUCK SWAP OVER  ( -- $2 $2 $1 $2)
           COUNT BOUNDS    ( -- $2 $2 $1 end start)
           DO
             I OVER COUNT S=  \ I steps thru $2 byte by byte
             0= IF
                  NIP I SWAP
                  LEAVE
             THEN
           LOOP
           DROP - ABS ;

\ =============================================
\ screen control
: SPACEBAR ( -- ) KEY? IF BEGIN KEY? UNTIL THEN ;

: ?BREAK-FILE ( hndl -- )
          ?TERMINAL
          IF CLOSE-FILE
             CR CR ." *BREAK*" ABORT
          ELSE
            DROP
          THEN ;

\ frequently used phrases
: OPEN  ( addr len -- ior ) OPEN-FILE ?FILERR ;
: CLOSE ( hndl -- )         CLOSE-FILE ?FILERR ;
: READH ( hndl -- )         READ-LINE ?FILERR 2DROP ;

: OPEN-CATFILE ( adr len -- hndl) RELATIVE 100 FIXED R/O BIN OPEN ;

\ 3 DIGIT BCD to int convertor. Limited to 999
HEX
: F>INT   ( addr len -- addr len n)
          OVER LEN  ( -- mantissa)
          CASE
            0 OF  0                    ENDOF
           40 OF  OVER 1+ C@           ENDOF
           41 OF  OVER 1+ C@ 64 * >R
                  OVER 2+ C@  R> +     ENDOF
           ( default)  -1  \ bad # indicator
           ENDCASE ;

DECIMAL
: DIR.TYPE  ( addr -- )
          F>INT
          CASE
             1 OF ." Txt/Fix"  ENDOF
             2 OF ." Txt/Var"  ENDOF
             3 OF ." Bin/Fix"  ENDOF
             4 OF ." Bin/Var"  ENDOF
             5 OF ." Program"  ENDOF
             ." ????"
          ENDCASE ;

: HEAD.REC ( addr -- )
          DECIMAL
          DUP  7 $.LEFT SPACE COUNT ( addr len)
          NEXT$
          ."  Size " NEXT$ F>INT 5 U.R   ."  Used " NEXT$ F>INT 5 U.R
          2DROP ;

: DIR.REC ( addr -- )
          DUP  11 $.LEFT SPACE COUNT ( addr len)
          NEXT$ DIR.TYPE
          NEXT$ F>INT 7 U.R
          NEXT$ F>INT 7 U.R
          2DROP ;

: .FILE# ( n -- ) DUP .  ." File" 1 <> IF ." s"  THEN ;


\ ========================================
\ *
\ * User commands: CAT DIR MORE DEL COPY
\ *

: CAT  ( <DSK?.> )   \  needs the '.' ONLY shows file name
          BASE @ >R DECIMAL
          ARG$ 2DUP DEV./ OPEN-CATFILE >R  \ store file handle
          /FILENAME SRC$ PLACE

          PAD 80 R@ READH
          CR PAD HEAD.REC
          CR 13 SPACES  ." -type-  -sect- -b/rec-"

          LINES OFF
          BEGIN
             PAD DUP 80 R@ READH
           ( PAD) LEN   \ while len > 0
          WHILE
             SRC$ PAD POS$
             IF
               CR PAD DIR.REC             
               1 LINES +!
             THEN
             SPACEBAR
             R@ ?BREAK-FILE
          REPEAT
          R> CLOSE
          CR LINES @ .FILE# CR
          R> BASE ! ;

HEX
: DIR  ( <DSK?.> )
          ARG$ 2DUP
          DEV./ OPEN-CATFILE >R  \ push handle
          /FILENAME SRC$ PLACE
          PAD 50 R@ READH
          CR PAD HEAD.REC CR

          LINES OFF
          BEGIN
            PAD DUP 80 R@ READH
          ( PAD) LEN   \ while len > 0
          WHILE
             SRC$ PAD POS$
             IF
               PAD 0C $.LEFT ?CR
               1 LINES +!
             THEN
             SPACEBAR
             R@ ?BREAK-FILE
          REPEAT
          R> CLOSE
          DECIMAL
          CR LINES @ .FILE# CR
          HEX ;

: MORE  ( <filename>)
          ARG$ DV80 R/O OPEN >R
          BEGIN
             PAD DUP 50 R@ READ-LINE ?FILERR ( adr len flag)
          WHILE
             CR TYPE
             1 LINES +!
             SPACEBAR
             R@ ?BREAK-FILE
          REPEAT
          R> CLOSE
          2DROP
          BASE @ >R DECIMAL
          CR LINES @ . ." Lines" CR
          R> BASE ! ;

: DEL   ( <filename>) ARG$ DELETE-FILE ?FILERR  ;

: COPY-FILE  ( addr len addr len -- )
        DV80 W/O OPEN TO #2
        DV80 R/O OPEN TO #1
        52 DUP MALLOC >R
        LINES OFF
        SPACE

        BEGIN
          R@  50  #1 READ-LINE ?FILERR ( -- #bytes eof?)
        WHILE
          R@ SWAP #2 WRITE-LINE ?FILERR
          LINES 1+!
          SPIN
        REPEAT
        R> DROP                 \ DROP buffer address from rstack
      ( 52) MFREE               \ release the buffer memory
        #2 CLOSE
        #1 CLOSE
        BASE @ >R 
        DECIMAL
        CR ." Copy complete. " LINES @ . ." records"
        R> BASE ! ;

: COPY  ( <file1> <file2> ) 
        ARG$ ARG$ 
        DST$ PLACE
        SRC$ PLACE

        DST$ COUNT /FILENAME NIP
        0= IF
           SRC$ COUNT /FILENAME DST$ +PLACE
        THEN
        SRC$ COUNT  DST$ COUNT COPY-FILE ;

: CLOSE-ALL  ( -- ior )
         0                       \ place holder for error codes
         #FILES @ 1+  1
         DO
           I ]FID @
           IF  I CLOSE-FILE OR   \ or the errors together
           THEN
         LOOP  ?FILERR ;

: CLS   PAGE ;

: HELP  CR
        CR ." Commands"
        CR ." --------------------"
        CR ." DIR  <DSK?.> show file names"
        CR ." CAT  <DSK?.> show files and types"
        CR ." MORE <path>  show contents of DV80 file"
        CR ." DEL  <path>  delete file at path"
        CR ." COPY <path1> <space> <path2> "
        CR ."      Copy file at path1 to path2"
        CR ." CLS  Clear screen"
        CR ." BYE  Return to E/A Menu"
        CR ." HELP Show command list" CR
        CR ." Any key will stop scrolling"
        CR ." FNCT 4 halts operation"
;

: SHELL
    L0 LP !                     \ init LEAVE stack pointer LP
    RP0 RP!                     \ reset rstack
    RSTPAB CLOSE-ALL            \ reset PAB stack in VDP RAM
    POSTPONE [                  \ STATE = 0 (Interpreting mode)
    BEGIN
      CR ." >" TIB DUP TIB# ACCEPT SPACE ( -- adr len) \ accept input to TIB, maxlen=TIB#
      INTERPRET                      \ interpret reads the stack string
      STATE @ 0= IF  ."  ok"  THEN   \ if we are not compiling print OK
    AGAIN ;

: START   
       80 83C2 C!
       ORGDP @ DP !
       ORGLAST @ LATEST !
       26 TPAD !
       2000 H !                       \ reset the heap
       TMR!                           \ set 9901 timer to count continously
       2 KUNIT# C!                    \ keyboard #2 is the BASIC keyboard
       ['] <INTERPRET> 'INTERPRET !
       DECIMAL
       TEXT ." Forth Shell V0.2"  HELP SHELL  ;

\ patch shell as main interpreter, set dictionary pointers
' SHELL ' ABORT 6 CELLS + !
' START  BOOT !
DP @ ORGDP !
LATEST @ ORGLAST !

 


Binary files are here now:

 

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

SHELL99.zip

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

Shell Update

 

Well... adding REGEX is kind of a big deal if you have ever tried to write one however how about simple string matching?

From the CAMEL99 string package there is a routine called POS$. Sounds familiar to a TI BASIC programmer.

 

So used POS$ to allow file name matching for the CAT and DIR commands.

 

Something interesting (to me) was creating path-name cutters with the Forth word SCAN. Scan takes an address, a length in bytes and an ASCII character.

It will give you a new address and len (ie a new string) cut at that location of the character. It's like LEFT$ and POS$ together.

The reciprocal word in Forth is /STRING which cuts a stack string address/length pair but the a number you give it on the stack. It's like RIGHT$ in BASIC.

 

Using Scan it was pretty simple to create a word to cut the device and word to cut the filename.

\ file path cutters
: /.        ( caddr len -- caddr' len' )  [CHAR] . SCAN ; \ cut at '.' Common factor

: DEV./     ( caddr len -- dev. len' )    2DUP /. NIP - 1+ ;

: /FILENAME ( caddr len -- filename len') /. 1 /STRING ;

Using these words it was pretty simple to add filename smarts to CAT and DIR.

 

I also changed COPY so that if you say:

COPY DSK1.MYFILE  DSK2. 

it will make a new file on DSK2. called MYFILE.

If you give a new filename for DSK2. it will use that new file name.

(I need to add protection from copying the same filename to the same disk!)

 

Here is Version 0.2 in operation. I will replace the binary file in the earlier post.

 

https://www.youtube.com/watch?v=9JCWTxiSRWw&feature=youtu.be

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

Speaking of SAMS I have now the ability to change any 4K section including entire 32K from XB.

 

The trick is done from GPL that does not need 32K and my BATCH file program CALL USER("device.filename") ! DV80 File

 

As CALL USER runs from VDP buffer (>03C0) and GPL it does not care what RAM is swapped out or changed.

 

USER loads a single line into VDP buffer and runs that line without needing any other instructions.

 

This one is going to be tough to beat for innovation of changing RAM with no access need for RAM.

Edited by RXB
Link to comment
Share on other sites

Speaking of SAMS I have now the ability to change any 4K section including entire 32K from XB.

 

The trick is done from GPL that does not need 32K and my BATCH file program CALL USER("device.filename") ! DV80 File

 

As CALL USER runs from VDP buffer (>03C0) and GPL it does not care what RAM is swapped out or changed.

 

USER loads a single line into VDP buffer and runs that line without needing any other instructions.

 

This one is going to be tough to beat for innovation of changing RAM with no access need for RAM.

 

I think it's impossible with anything but GPL unless you replace GPL with a different interpreter.

I don't think I will miss the lower section of SAMS. It takes Forth 20 secs to erase the leftover that I can use.

That's like an I/O device rather than memory! :woozy:

 

But I think I can use it to built an editor capable of editing large files.

 

BTW how long does it take GPL to erase the SAMS card? (Well most of it: 1M-64K was my test)

Link to comment
Share on other sites

 

I think it's impossible with anything but GPL unless you replace GPL with a different interpreter.

I don't think I will miss the lower section of SAMS. It takes Forth 20 secs to erase the leftover that I can use.

That's like an I/O device rather than memory! :woozy:

 

But I think I can use it to built an editor capable of editing large files.

 

BTW how long does it take GPL to erase the SAMS card? (Well most of it: 1M-64K was my test)

Hmm why would you need to ERASE the card ?

I mean how often does that need to be done?

 

I just overwrite what is there but with 1 Meg of space, not like anyone has ever filled the card with 1 Meg yet?

 

My INTHE DARK game in RXB used more of the SAMS then anyone previously ever did at 480K.

Can you name anyone that wrote anything using over 400K of SAMS RAM memory?

Link to comment
Share on other sites

 

It was a way to see how fast I could write to the card. It's just a benchmark.

Oh, well GPL is not really doing the work I have a imbedded Assembly routine that does the work.

GPL CODE:


*****************************************************
* SAMS replaced AMSPASS, AMSMAP, AMSOFF, AMSON      *
* CALL SAMS("PASS",...)                             *
* CALL SAMS("MAP",...)                              *
* CALL SAMS("OFF",...)                              *
* CALL SAMS("ON",...)                               *
***************************************************** 
* SAMS replaced AMSBANK full RAM memory management  *
***************************************************** 
* CALL SAMS(2,page,3,page,A,page,B,page,C,page,     * 
* D,page,E,page,F,page,...)                         *
*                                                   *
* Numbers 2 is >2000, 3 is >3000                    *
* Letters A is >A000, B is >B000, C is >C000        *
* Letter  D is >D000, E is >D000, F is >F000        *
* page now is SAMS 4K pages from 0 to 255           *
*****************************************************
* LINK replaced EXECUTE so LINK will use address    *
*****************************************************
* BSAVE and BLOAD replaced with full memory address *
* 4K RAM boundries same as SAMS addressing RAM      *
*****************************************************
SAMS   DATA CHRALL
       STRI 'SAMS'        
       DATA $+2
       CALL COMB            * ( ?
**************************************************
* Get stirng or token or numeric                 *
* String is for PASS,MAP,OFF, ON                 *
* 2 and 3 are numeric as no token exist for them *
* thus need a numeric interpetation for 2 and 3  *
* A, B, C, D, E, F are tokenized already for use *
**************************************************
SAMS2  XML  PGMCHR          * Skip ( OR COMMA 
       CEQ  >C7,@CHAT       * STRING?
       BR   SAMSPS          *  Must be a TOKEN?
SAMSTR CALL STRPAR          * GET STRING?      
       CEQ  >65,@FAC2       * STRING?
       BR   ERRBV           * ERROR BAD VALUE
       DCZ  @FAC6           * 0 Length?
       BS   ERRBA           * ERROR BAD ARGUMENT 
       DCEQ >5041,V*FAC4    * PA? PASS MODE
       BR   AMSMAP          * SAMS MAP
* CALL AMSPASS ************** 
       CALL PASAMS          * SAMS PASS
       BR   SAMS3           * CHECK FOR COMMA
AMSMAP DCEQ >4D41,V*FAC4    * MA? MAP MODE
       BR   AMSOFF          *SAMS OFF
* CALL AMSMAP ***************
       CALL MAPAMS          * SAMS MAP 
       BR   SAMS3           * CHECK FOR COMMA
AMSOFF DCEQ >4F46,V*FAC4    * OF? SAMS OFF
       BR   AMSON           * SAMS ON
* CALL AMSOFF ***************
       CALL OFFAMS          * AMS OFF
       BR   SAMS3           * CHECK FOR COMMA
AMSON  DCEQ >4F4E,V*FAC4    * ON? SAMS ON
       BR   ERRBA           * ERROR BAD ARGUMENT 
* CALL AMSON ****************
       CALL ONAMS           * AMS ON
       BR   SAMS3           * CHECK FOR COMMA   
******************************************************
* Moves 12 bytes ASSEMBLY into >8300 Scratch Pad RAM *
* Executes address at >8300 BLWP FAC & ARG workspace *
******************************************************
PASAMS CALL AMSSUB            * AMS PASS SUBROUTINE
       DST  >1E01,@STORE      * LOAD PASS VALUE 
       BR   SAMSUB            * EXECUTE IT
ONAMS  CALL AMSSUB            * AMS ON SUBROUTINE
       DST  >1D00,@STORE      * LOAD ON VALUE
       BR   SAMSUB            * EXECUTE IT
OFFAMS CALL AMSSUB            * AMS OFF SUBROUTINE
       DST  >1E00,@STORE      * LOAD OFF VALUE
       BR   SAMSUB            * EXECUTE IT
MAPAMS CALL AMSSUB            * AMS MAP SUBROUTINE
       DST  >1D01,@STORE      * LOAD MAP VALUE
SAMSUB XML  >F0               * EXECUTE ASSEMBLY
       RTN                    * RETURN
**********************************************************
* MOVES CPU PROGRAM TO SCRATCH PAD                       *
AMSSUB MOVE 18,G@AMSCRU,@>8300 * GET ASSEMBLY FROM GROM  *
       RTN                     * RETURN                  *
**********************************************************
* SAMS PAGE CHANGE    
******************************************************
* SAMS PAGES 2,3,A,B,C,D,E,F TOKENS                  *
* PAGES range from 0 to 255 now instead of 16 to 255 *
* Also now all SAMS RAM range not just lower 8K      *
******************************************************
SAMSPS CALL SAMS4A         * ADDRESS IN TEMP & PUSHED 
       CEQ  COMMAZ,@CHAT   * COMMA?
       BR   ERRSYN         * ERROR SYNTAX
       XML  PGMCHR         * Skip COMMA
       CALL STRPAR
       XML  CFI            * PAGE Convert to integer
       CALL MAPAMS         * AMS MAP
       CALL ONAMS          * AMS ON
* TEMP has RAM address >A000 up to >F000 
* Shift address to be 2* value for SAMS register
* i.e. >F0 would be >1E so >401E would be register
       SRL  3,@TEMP        * MOVE TO LOWER NIBBLE
       EX   @TEMP+1,@TEMP  * SWAP byte locations
       ST   @FAC1,@4000(@TEMP) * SET PAGE
       CALL OFFAMS        * AMS OFF
SAMS3  CEQ  COMMAZ,@CHAT  * COMMA?
       BS   SAMS2
SAMS4  CEQ  RPARZ,@CHAT   * )?
       BR   ERRSYN        * SYNTAX ERROR
       XML  PGMCHR        * Skip ")"
       CALL RETURN        * RETURN TO CALLER
****************************************************
* SAMS PAGES 2,3,A,B,C,D,E,F                       *
* Get 2 and 3 numeric or A to F tokens             *
* input in CHAT is >C8 is numeric or must be token *
* output TEMP has RAM ADDRESS of 4K page to save   *
****************************************************
SAMS4A CEQ  >C8,@CHAT  * NUMBER?
       BR   SAMSAL     * No must be 2 or 3 or A to F 
       CALL STRPAR     * Get number 
       XML  CFI        * Convert to integer
       CHE  4,@FAC1    * 1 or higher
       BS   ERRBV      * ERROR BAD VALUE
       DST  >2000,@TEMP * Defualt address
       CEQ  2,@FAC1    * 2?
       BS   SAMSP3     * Ok so exit
       CHE  4,@FAC1    * 4 or higher?           
       BS   ERRBV      * ERROR BAD VALUE
       ADD  >10,@TEMP  * Get address 
SAMSP3 RTN             * RETURN
* 24K ADDRESS PAGES
SAMSAL CHE  >47,@CHAT  * G OR HIGHER
       BS   ERRBA      * ERROR BAD ARGUMENT
       CHE  >41,@CHAT  * A OR HIGHER?
       BR   ERRBV      * ERROR BAD VALUE
       ST   @CHAT,@ARG * Save TOKEN
       SUB  >41,@ARG   * 0 TO 5
       DST  >A000,@TEMP * Default value
SAMSLP CZ   @ARG       * 0?
       BS   SAMSD      * RETURN 
       ADD  >10,@TEMP  * >B000 TO >F000 
       DEC  @ARG       * 5 TO 1
       B    SAMSLP     * LOOP FOREVER
SAMSD  XML  PGMCHR     * SKIP TOKEN
       RTN             * RETURN
**********************************************************

Assembly code:

***********************************************************
* CPU PROGRAM FOR >8300 SCATCH PAD SUBROUTINE AMSCRU      *
***********************************************************
*                 *        AORG >8300
AMSCRU DATA >8302 * AMSCRU DATA >8302     * First address.
       DATA >C04C *        MOV  R12,R1    * Save R12 
       DATA >020C *        LI   R12,>1E00 * Load CRU bits
       DATA >1E00 *
       DATA >1D00 *        SBO  0         * Set bits ones
       DATA >C301 *        MOV  R1,R12    * Restore R12
       DATA >04E0 *        CLR  @>837C    * Clear for GPL
       DATA >837C *
       DATA >045B *        RT             * Return to GPL.
                  *        END
***********************************************************
* CPU PROGRAM FOR >8300 SCRATCH PAD CPU ISR HOOK ON       *
***********************************************************
*                  *        AORG >8300
GISRON DATA >8302  *        DATA >8302
       DATA >C820  *        MOV  @>834A,@>83C4
       DATA >834A  *
       DATA >83C4  *
       DATA >04E0  * EXIT   CLR  @>837C
       DATA >837C  *
       DATA >045B  *        RT
*                  *        END
***********************************************************
* CPU PROGRAM FOR >8300 SCRATCH PAD CPU ISR HOOK OFF      *
***********************************************************
*                          AORG >8300
GISROF DATA >8302 *        DATA >8302
       DATA >C820 * ISROFF MOV  @>83C4,@>83C4
       DATA >83C4 *
       DATA >83C4 *
       DATA >1305 *        JEQ  NHOOK
       DATA >C820 *        MOV  @>83C4,@>834A
       DATA >83C4 *
       DATA >834A *
       DATA >04E0 * NHOOK  CLR  @>83C4
       DATA >83C4 *
       DATA >04E0 *        CLR  @>837C
       DATA >837C *
       DATA >045B *        RT
*                 *        END
***********************************************************
* CPU PROGRAM FOR >8300 SCATCH PAD SUBROUTINE EXECUTE     *
***********************************************************
*                          AORG >8300
CPUPGM DATA >8302 * CPUPGM DATA >8302  First address. *
       DATA >0420 *        BLWP @>834A Switch contex  *
       DATA >834A *                    FAC not used   *
       DATA >04E0 *        CLR  @>837C Clear for GPL  *
       DATA >837C *                                   *
       DATA >045B *        RT          Return to GPL. *
                  *        END                        *
***********************************************************
Link to comment
Share on other sites

Ah yes. I see some familar ASM code there.

 

I have never tried GPL, but I see that, like Forth, you use machine code to insert the Assembler output into the code.

 

in Forth I have to load the assembler, assemble the ASM code, then use DUMP to see the machine code.

Then I have to manually type the machine code data back into my source file.

That's not to hard on a windows machine, but would be a total PITA on real hardware.

 

I have a project on the stack that will let you output the machine code for any "code" word to a file, in source code format, so you can insert the file into the editor.

I can already do the output to the screen, I just need to get some vectored I/O working to send output to a file.

 

So much code, so little time.

  • Like 1
Link to comment
Share on other sites

Rich, you got me thinking. I was spinning on adding i/o re-direction to CAMEL99 Forth which would mean changing the kernel quite a bit.

Then I realized that what I wanted was a way to get the text from the screen into a file.

 

The VDP screen is just a big text buffer! So why not just capture the screen to a file.

In real hardware save it to disk. In CLASSIC99 save it to CLIP and paste into your code.

 

Now that this little system has a healthy bunch of widgets it took very little to make a screen capture utility

\ ti-99 screen capture utility for CAMEL99 Forth  BJF Jan 2019

NEEDS WRITE-LINE FROM DSK1.ANSFILES

VARIABLE HNDL   \ holds file handle

: (CAPTURE) ( -- )
         VROW 2@ 2>R  \ save screen xy
         L/SCR 0      \ capture all lines
         DO
           0 I AT-XY VPOS PAD C/L@ VREAD
           PAD C/L@  HNDL @ WRITE-LINE ?FILERR
         LOOP
         HNDL @ CLOSE-FILE ?FILERR 
         2R> AT-XY ;  \ restore screen xy

: CAPTURE  ( <PATH> )
           BL PARSE-WORD  ( -- $addr len )
           DV80  W/O OPEN-FILE ?FILERR HNDL !
           (CAPTURE) ;

post-50750-0-95534800-1548257815.jpg

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

Ah yes. I see some familar ASM code there.

 

I have never tried GPL, but I see that, like Forth, you use machine code to insert the Assembler output into the code.

 

in Forth I have to load the assembler, assemble the ASM code, then use DUMP to see the machine code.

Then I have to manually type the machine code data back into my source file.

That's not to hard on a windows machine, but would be a total PITA on real hardware.

 

I have a project on the stack that will let you output the machine code for any "code" word to a file, in source code format, so you can insert the file into the editor.

I can already do the output to the screen, I just need to get some vectored I/O working to send output to a file.

 

So much code, so little time.

 

 

fbForth has ASM>CODE (ported from TurboForth’s word of the same name), which might guide |:) your composing such a word in Camel99 Forth. It starts in block #39 of FBLOCKS and is described on page 182 in the glossary of the manual.

 

...lee

  • Like 1
Link to comment
Share on other sites

TheBF

 

Well the most bytes I put into Scratch RAM for SAMS is 18 bytes. (No assembler needed for that few bytes)

And I use FAC & ARG as Assemlby WorkSpace thus FAC (>834A) contains the value passed to or from XB in Register 0.

Value from XB is converted first with CFI and from Assembly to XB is CIF.

Which by convenience sake is in FAC for both.

 

Works out very little code is needed to do alot.

Link to comment
Share on other sites

 

 

fbForth has ASM>CODE (ported from TurboForth’s word of the same name), which might guide | :) your composing such a word in Camel99 Forth. It starts in block #39 of FBLOCKS and is described on page 182 in the glossary of the manual.

 

...lee

 

Thanks Lee. I will take a look at it. I have something working, but's always good to compare notes.

Link to comment
Share on other sites

There is a sacred commandment in Forth that goes:

 

"Thou shalt never ROLL".

 

 

ROLL is a word that re-organizes the stack arguments in a circular fashion. ( 1 2 3 4 3 ROLL becomes 2 3 4 1

It's typically very slow and indicates that you didn't plan your code well if you are delivering arguments in messed up order to your routines.

 

However …

While reading through FBForth's block source code I found this GEM and the cleverness of it deserves a mention.

(Recursive code always make me quiver a little)

 

I modified it just slightly to be ANS Forth and added comments to pacify my aching brain a little.

( The source for ROLL was Marshall Linker via George Smyth's Forth Forum)

 : ROLL ( [n]..[0] +n -- [n-1]..[0][n] )
        ?DUP            \ DUP TOS if <> 0  (TOS = top of stack)
        IF              \ if that's true...
           1- SWAP >R   \ decrement TOS , push to Return stack
           RECURSE      \ call ROLL
           R>  SWAP     \ POP the return stack and SWAP with TOS
        THEN  ;

To learn the definition of RECURSE, see RECURSE :-D

  • Like 2
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...