Jump to content
IGNORED

Camel99 Forth Information goes here


TheBF

Recommended Posts

Rosetta Code Clock

 

I noticed nobody in the Forth community put up an entry in Rosetta code for a clock so I took some pieces from CAMEL99 Forth and created one.

 

It uses the big letter printing demo, plus the .TIME from the ELAPSED program. I put in the caveat that the code requires an interrupt driven timer

with a 1/60 second interval. It makes a pretty good timer to just wait for the byte at >8379 to change.

 

I had to include a small pattern description table, because the code might be run on another machine than the TI-99.

 

The one neat thing about the code is I use M+ , which takes 2 ,16 bit integers and adds them to create a 32 bit result.

With this I can keep track of 4.1 billion seconds or so on the clock. :)

 

Here is the code:

 

http://www.rosettacode.org/wiki/Draw_a_clock#Forth

  • Like 1
Link to comment
Share on other sites

 

 

I must be missing something, but how does your code comply with rule #3 re hogging CPU time?

 

...lee

Quite right.

 

I considered making it multitasking, but that is not supported in standard Forth.

 

I think to address that I will add PAUSE to the counter loop and note that this prevents CPU hogging.

\  subtract old value from new value until ticker changes.
: 1/60  ( -- )
        TICKER DUP @  ( -- addr value)
        BEGIN
             PAUSE    \ *Gives time to other Forth processes while we wait
             OVER @   \ read ticker addr
             OVER -   \ subtract from old value
        UNTIL
        2DROP ;

I just made the change. Thanks for keeping me honest.

Edited by TheBF
Link to comment
Share on other sites

I think I would code it as/with a user ISR in fbForth, but, then again, I suppose not all Forths support that either.

 

...lee

 

You can always specify which version of Forth is being demonstrated and hi-light FbForth directly.

 

I have been reviewing this code with GForth and I found that yet there more dependencies.

The character table is big-endian for the 9900 so I need to add that and specify a 16 bit CPU as well.

It's never easy with such a low level language. :(

  • Like 1
Link to comment
Share on other sites

And one more gotcha that applies to both of our systems.

I have been playing with a very specific TI-99 DEMO of this concept and the forth number conversion buffer is not thread-safe.

I had the Forth console running below the clock and of course when I display a number at the console, the clock messes up.

 

You need to use a specific buffer for each thread. I made a way to do this but broke it by trying to optimize the system with a code word for PAD. :mad:

 

Edit: I forgot that I had to set a variable in the new task to put the local PAD above Forth's PAD. duh!

Edited by TheBF
Link to comment
Share on other sites

Re the PAD thing: I would need to do something like was done with the DEMO user ISR in Chapter 10 of my manual. DEMO was inherited from the TI Forth manual. It defines the following words to move PAD up before and back down after running the ISR:

: UP 100 ALLOT ;       \ move HERE and thus PAD up 100 bytes
: DOWN -100 ALLOT ;    \ restore PAD to its original location

...lee

  • Like 1
Link to comment
Share on other sites

TTY Terminal for CAMEL99 Forth

 

Ever since I got a copy of TI Forth back in 1984 (85?) I wanted to be able to use the RS232 ports to control the TI.

My PC keyboard was always nicer to work with and I had 2 serial ports on the darn machine just sitting there since my dot matrix printer ran on the PIO port.

 

I have been studying on how best to this and keep the whole thing in an 8K kernel. It turns out that Assembler takes a lot less space for CRU operations since I don't need the overhead of the Forth CRU commands loaded into the system. And as I have mentioned the semantic power of 9900 Forth assembler is about the same as Forth for low level operations anyway and it goes faster.

 

The big concern is always dropping characters on receive. With the little receive routine in the spoiler I found I could send continuous characters at 9600 and echo them onto the VDP screen and only drop chars during a screen scroll. So for typing this is no concern.

 

For sending text programs to Forth I need to wait for the echo back from Forth on each character and add a small delay on a newline. If I do that I will never overrun the input. I have done this in the past with 68HC11 embedded Forth and it works fine.

 

The weird thing is how much more responsive the keyboard is without all that code the runs in KSCAN.

It's not bullet proof yet but I will get it there because I like this way of working. The old CRT monitor will have to move over to this desk now.

 

Note: my configuration code below is very ugly but I was just trying to make a compact word. It's on the chopping block.

 

 

 

\ xfcc99 cross-compiler tms9902 rs232/1 DIRECT cru DRIVER 27jan2019 bjf
\ CODE words are used to save space by not needing the CRU2 library

[CC] HEX [TC]
               1300 CONSTANT: RS232/1    \ card address
[CC] 1300 40 + [TC] CONSTANT: /TTY1       \ 40= 9902#1,
[CC] 1300 80 + [TC] CONSTANT: /TTY2       \ 80= 9902#2

\ 9902 control bits
[CC] DECIMAL [TC]
      13 CONSTANT: LDIR      \ load interval register
\ for reference...
\      16 CONSTANT: RTSON    \ request to send
\      18 CONSTANT: RIENB    \ rcv interrupt enable
\      21 CONSTANT: RXRL     \ receive register loaded bit
\      22 CONSTANT: TXRE     \ transmit register empty bit
\      27 CONSTANT: -DSR     \ NOT data set ready
\      28 CONSTANT: -CTS     \ NOT clear to send
       31 CONSTANT: RESET    \ RESET the UART

[CC] HEX [TC]
VARIABLE: BPS   0034 BPS T!
VARIABLE: PROTO B200 PROTO T!
VARIABLE: PORT  [CC] /TTY1 PORT T! [TC]

CODE: 9600,7,O,1 ( port --)
      TOS R12 MOV,
      RESET SBO,
      R1 B200 LI,
      R1  8 LDCR,
      LDIR SBZ,
      BPS @@ R2 MOV,
      R2 0C LDCR,
      TOS POP,
      NEXT,
      END-CODE

[CC] DECIMAL [TC]
CODE: TTYEMIT ( c -- )
          PORT @@ R12 MOV,
          BEGIN,
            27 TB,
          EQ UNTIL,   \ wait for DSR
          16 SBO,     \ set RTS

          BEGIN,
            22 TB,
          EQ UNTIL,   \ wait XBRE empty
          TOS SWPB,   \ put byte on the other side
          TOS 8 LDCR, \ send 8 bits
          16 SBZ,     \ reset RTS
          OUT @@ INC,
          TOS POP,
          NEXT,
          END-CODE

CODE: TTYKEY ( -- n ) \ fastest tty read
          PORT @@ R12 MOV,    \ select the 9902
          TOS PUSH,
          16 SBZ,             \ RTS off
          BEGIN,
            21 TB,            \ test if char ready
          EQ UNTIL,
          16 SBO,             \ RTS on to stop sending
          TOS 8 STCR,         \ read the char
          TOS 8 SRL,          \ shift to other byte
             18 SBZ,          \ reset char buffer
          NEXT,
          END-CODE

CODE: ?TERMINAL ( -- n )      \ fast tty read
          PORT @@ R12 MOV,    \ select the 9902
          TOS PUSH,
          TOS CLR,
          16 SBZ,             \ RTS off
          21 TB,              \ test if char ready
          EQ IF,
                 16 SBO,      \ RTS on to stop sending
              TOS 8 STCR,     \ read the char
              TOS 8 SRL,      \ shift to other byte
          ENDIF,
          18 SBZ,             \ reset char buffer
          NEXT,
          END-CODE

 

 

post-50750-0-53749700-1548728018.jpg

  • Like 2
Link to comment
Share on other sites

The rest of the TTY interface for those who are interested.

Is SIGNIFICANTLY smaller than doing the same thing for VDP and KSCAN

\ TTY OUTPUT is done with EMIT
: EMIT  ( char  -- ) \ replaced with code version: TI99PRIM.HSF
          TTYEMIT
          1  OUT +!
          1 VCOL +! ;

: CR    ( -- ?)
         0D EMIT 0A EMIT  
         OUT OFF  VCOL OFF
         1 VROW +!  ;

: PAGE    ( -- )  0C TTYEMIT  OUT OFF  0 0 AT-XY ;
: TYPE    ( adr cnt --) BOUNDS ?DO  I C@ EMIT  LOOP ;
: SPACE   ( -- )   BL EMIT ;
: SPACES  ( n -- ) 0 MAX  0 ?DO SPACE LOOP ;

\ =========================================
\ Forth input use KEY , which calls TTYKEY
: KEY        ( -- char) TTYKEY ;

\ High level: input/output                    (c) 31mar95 bjr
: ACCEPT      ( c-addr +n -- +n')            \ get line from terminal
             OVER + 1- OVER
             BEGIN  KEY DUP 0D <>            \ test for enter (hex D)
             WHILE
                DUP EMIT 
                DUP 8 =                        \ test for back-space character
                IF   DROP 1-  >R OVER R> UMAX  \ move the buffer pointer back
                ELSE OVER C!  1+ OVER UMIN
                THEN
             REPEAT
             DROP NIP SWAP -  ;
  • Like 1
Link to comment
Share on other sites

One day I may stop using a homemade compiler...

 

But in the meantime, I have Forth's interpreter running over RS232 beautifully but the compiler is broken.

The problem is somewhere in the routine that builds the dictionary data structure but I am damned if I understand why.

I also noticed that my stack dump word was broken as well. So I have two clues to find what I have broken.

 

 

post-50750-0-65193500-1548900967.jpg

Link to comment
Share on other sites

I have not spent enough time with your compiler to be able to offer much help. The only thing that comes to mind is whether any words in your TTY interface redefine existing words that might get called by the compiler or DUMP code. If that is the case, the original definitions might confuse things when they get executed instead of their new counterparts.

 

...lee

Link to comment
Share on other sites

I have not spent enough time with your compiler to be able to offer much help. The only thing that comes to mind is whether any words in your TTY interface redefine existing words that might get called by the compiler or DUMP code. If that is the case, the original definitions might confuse things when they get executed instead of their new counterparts.

 

...lee

That's a good thought. Since my first attempts I have redefined KEY and EMIT as the actual code words rather than calling them as primitives.

This greatly simplified things and the problem existed in the previous version.

 

But I think there is some hidden thing going on regarding my .S breaking and the HEADER word that makes the dictionary entry.

It points to some underlying thing that I don't know about.

 

I will double check against your idea to see what I missed there.

 

I will have to make a tool to display whats happening in the header work while it executes.

 

Brief story with my coffee:

 

I called the founder of New Micros, Randy Dumse, years ago to get some help with a agricultural project I built on one of his 68HC11 boards.

After my description he said "Well, it sounds like you know what you are doing. Did I give you my man is toolmaking animal lecture?"

 

I said "No".

 

He said "Well here it is."

 

"Man is a toolmaking animal. Make a tool."

 

and he hung up. :D

  • Like 3
Link to comment
Share on other sites

Dead bugs make me happy

 

So after doing a pretty big overhaul of the RS232 code I got things working. I still don't fully understand what was fooling the cross-compiler into making wrong code but it was no doubt an error on my part that does not flag an error from the cross-compiler during compilation. When I write correct code it seems to behave. I will find that little devil eventually.

 

I thought I would take some time to explain one way to do vectored I/O on a Forth system since that is what I have been striving to achieve.

I know that TI-Forth was capable of this but to be honest I did not understand it in the early 1980s. It was not until I bought a commercial Forth system that I got clarity on how it worked. Like all things inside Forth its simple.

 

Traditional Forth I/O

 

Forth systems have at a minimum only two I/O routines. More are possible, but for this explanation let's keep it to two.

 

They routines are called KEY and EMIT. KEY waits for an input character and EMIT sends a character.

For each computer system there is a way to do those simple functions so most of the time you just have to make a call to the operating system to get a character and send a character to the default I/O device.

 

On TI-99 of course it's the VDP chip that we use for EMIT so writing up a little routine that takes a byte from the Forth stack and puts it into VDP memory is pretty simple. It's mostly VSBW.

We just need a few variables to remember where the cursor is.

 

For Forth's KEY we literally branch and link to the KSCAN, read a character from the byte buffer and put it on the top of the Forth stack, ready for some code to use.

 

KEY and EMIT are then used throughout the system to write any routine that gets or sends characters to the "standard I/O" devices.

For example ACCEPT reads a string of characters and it uses KEY internally. TYPE prints a string and it uses EMIT internally.

If you consistently use KEY and EMIT in all your high level I/O routines everything just works. It's easy. It's not always the fastest, but it works.

 

Different I/O Devices

So all that is great until you want to use a different I/O channel. What to do?

 

The secret is to use variables to hold the "execution token" (XT) of an I/O routine and use EXECUTE to run that XT.

Below is an example of how EMIT can become different routines, but still have the name "EMIT" in your Forth program.

VARIABLE 'EMIT   ( Called "tick emit" Holder for the execution token, ie: the XT) 

\ This is all it takes to make a "vectored" EMIT routine
: EMIT  ( c -- ) 'EMIT @  EXECUTE ;  ( fetch the token from the variable and run it)

\ write the code to write a byte to different devices
: VDP-EMIT  ( c -- ) ( code like VSBW to put a character on the screen) ;
: PIO-EMIT  ( c -- ) ( BLAH BLAH BLAH CODE TO SEND BYTE TO PRINTER )  ;
: TTY-EMIT ( c --) ( CODE TO SEND BYTE TO SERIAL PORT ) ;

\ create commands to change the XT held by 'EMIT 
: >CONSOLE         [']  VDP-EMIT  'EMIT !  ;     \ lookup the XT of VDP-EMIT and store in variable 'EMIT
:  >PIO            [']  PIO-EMIT   'EMIT ! ;
:  >TTY            ['] TTY-EMIT   'EMIT ! ;

So in the attached video you are seeing this in action.

In this version of CAMEL99 Forth I have created "vector" variables for EMIT, KEY and CR (newline command).

 

I have written a Forth Assembler routine to send a byte called CEMIT (com emit).

There is also a routine called CKEY, (com key) which is multi-tasking friendly and there is also a routine called CCR ( com carriage return).

CCR is needed because a newline on a terminal send a carriage routine and line-feed character whereas on the TI-99 it moves the cursor in position in VDP memory. So I vectored that as well.

 

The end result is that I can send and receive data from Forth on the TI-99 over RS232 and that also means I can send source code and have it compile and/or interpreted from the PC as well. :)

 

There are more details to consider here but I thought someone might like to know how this works and where I am taking CAMEL99 Forth.

9600RTS - HyperTerminal 2_4_2019 10_59_08 AM.mp4

post-50750-0-75370100-1549300690.jpg

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

...

Different I/O Devices

So all that is great until you want to use a different I/O channel. What to do?

 

The secret is to use variables to hold the "execution token" (XT) of an I/O routine and use EXECUTE to run that XT.

Below is an example of how EMIT can become different routines, but still have the name "EMIT" in your Forth program.

VARIABLE 'EMIT   ( Called "tick emit" Holder for the execution token, ie: the XT) 

\ This is all it takes to make a "vectored" EMIT routine
: EMIT  ( c -- ) 'EMIT @  EXECUTE ;  ( fetch the token from the variable and run it)

\ write the code to write a byte to different devices
: VDP-EMIT  ( c -- ) ( code like VSBW to put a character on the screen) ;
: PIO-EMIT  ( c -- ) ( BLAH BLAH BLAH CODE TO SEND BYTE TO PRINTER )  ;
: TTY-EMIT ( c --) ( CODE TO SEND BYTE TO SERIAL PORT ) ;

\ create commands to change the XT held by 'EMIT 
: >CONSOLE         [']  VDP-EMIT  'EMIT !  ;     \ lookup the XT of VDP-EMIT and store in variable 'EMIT
:  >PIO            [']  PIO-EMIT   'EMIT ! ;
:  >TTY            ['] TTY-EMIT   'EMIT ! ;

...

 

Ti Forth and fbForth have two user variables for alternate I/O for KEY and EMIT . They are ALTIN and ALTOUT for KEY and EMIT , respectively. If the value is not 0, then it must point to the PAB address in VRAM of the I/O device—usually RS232 or PIO, which must have a one-byte I/O buffer immediately preceding the PAB.

 

However, if a facility similar to the one you describe for Camel99 Forth were to be provided for Ti Forth and fbForth, it would have the following code à la figForth/Forth-79:

VARIABLE 'EMIT   ( Called "tick emit" Holder for the execution token, ie: the XT) 

\ This is all it takes to make a "vectored" EMIT routine
: EMIT  ( c -- ) 'EMIT @  EXECUTE ;  ( fetch the token from the variable and run it)

\ write the code to write a byte to different devices
: VDP-EMIT  ( c -- ) ( code like VSBW to put a character on the screen) ;
: PIO-EMIT  ( c -- ) ( BLAH BLAH BLAH CODE TO SEND BYTE TO PRINTER )  ;
: TTY-EMIT  ( c -- ) ( CODE TO SEND BYTE TO SERIAL PORT ) ;

\ create commands to change the XT held by 'EMIT 
: >CONSOLE  ' VDP-EMIT CFA 'EMIT ! ;   \ lookup the XT of VDP-EMIT and store in 'EMIT
: >PIO      ' PIO-EMIT CFA 'EMIT ! ;   \ lookup the XT of PIO-EMIT and store in 'EMIT
: >TTY      ' TTY-EMIT CFA 'EMIT ! ;   \ lookup the XT of TTY-EMIT and store in 'EMIT

...lee

  • Like 1
Link to comment
Share on other sites

Thanks for the translation code Lee. I now remember seeing ALTIN and ALTOUT in screen code. I must have got it working because my old dot matrix printer worked.

 

So after I got the colon compiler working and the vectored I/O and did this little video recording I was very pleased and posted here.

 

Next I tried loading a file from TI disk... <ugly music plays here> :woozy:

 

I am looking over some things to find the error in my ways. I am using the same DSR file for both kernel builds however.

I may have to resort to building a TI-99 kernel, add in the Vectored Serial I/O, save it all with Classic99's utility and see what gives from within Forth on the standard I/O devices on the old hardware.

 

My amateur status is showing. It's a humbling hobby as I have said numerous times before.

  • Like 1
Link to comment
Share on other sites

I am considering opening a new store called Bugs R Us

 

In my previous serial port testing I had included a "tool" file that I had used in the past for working on the system. It is written in Cross-compiler Forth and I thought it was bug free.

 

I removed it from the build to simplify things and lo and behold I can now compile code from floppy disk, while controlling the TI-99 over the serial port.

 

I have simplified the overall code that does the interface. To support multi-tasking I need to have the receive routine KEY written in Forth, so the ALC code word does not loop, rather it tests one bit to see if there is a character in the 9902 RX register. If there is it reads it to the top of Forth stack. If not it just exits. This allows a very simple and classic implementation of KEY in Forth.

 

I don't know if it matters but I am saving R12 on the Forth return stack and restoring it between operations. This is actually slightly faster than that using BLWP (28+22=40 cycles)

I did this to eliminate any possibility of corrupting other I/O operations while talking over RS232.

 

Question: Ideally I want to handshake off when I detect a character since I don't have receive interrupts. Not sure how that is done. (set RTS on maybe?)

CODE: CKEY? ( -- n )          \ fast tty read
          R12 RPUSH,
          PORT @@ R12 MOV,    \ select the 9902
          TOS PUSH,
          TOS CLR,
          21 TB,              \ test if char ready
          EQ IF,
              TOS 8 STCR,     \ read the char
             TOS 8 SRL,       \ shift to other byte
                18 SBZ,       \ reset char buffer
          ENDIF,
          R12 RPOP,
          NEXT,
          END-CODE

: ?TERMINAL ( -- ?) CKEY? 3 = ;       \ ^C will be "break" key

: KEY        ( -- char) 
              BEGIN
                PAUSE                 \ multi-tasking switch
                CKEY?                 \ test for key
                ?DUP                  \ dup if not zero
              UNTIL ;                 \ char is on stack if loop exits

For anyone who needs a programmable terminal for Windows I have found Terra Term. It is very good. One downside, it does not seem to have an Xmodem transfer mode for use with Magic File Manipulator.

 

The attached video shows compiling from floppy disk while using Terra Term to control the machine. I couldn't help configuring it to look like TI BASIC. :-)

 

 

 

 

COM1 - Tera Term VT 2_6_2019 8_47_14 AM.mp4

  • Like 1
Link to comment
Share on other sites

I am having some fun (and bugs) controlling Forth over the serial port. The final code to access the serial port is in the spoiler below.

I only flash the RS232 CARD led during transmit because that is under TI-99 control. On receive, without interrupts I want the code as lean and fast as possible.

 

NOTE: This is not ANS Standard Forth, it is my CROSS-COMPILER Forth which uses some special incantations to tell Forth when to compile for TI-99 and when to change things in the PC that's doing the compiling. I changed some standard words like 'constant' to have an extra colon to help me keep track of which machine I am talking to in the code. (I need all the help I can get)

 

 

 

\ xfcc99 cross-compiler tms9902 rs232/1 DIRECT cru DRIVER 9Feb2019 bjf
\ CODE words are used to save kernel space by not needing the CRU library

\ These routines protect the value in R12 on the return stack
\ then restore it when returning to Forth.
\ This should help with acessing other I/O devices while using
\ the serial port.

[CC] HEX [TC]
\               1300 CONSTANT: RS232/1    \ card address
[CC] 1300 40 + [TC] CONSTANT: /TTY1       \ 40= 9902#1,
\ [CC] 1300 80 + [TC] CONSTANT: /TTY2       \ 80= 9902#2

\ 9902 control bits
[CC] DECIMAL
       13 EQU LDIR           \ "load interval register"
\ for reference...
\      16 CONSTANT: RTSON    \ request to send
\      18 CONSTANT: RIENB    \ rcv interrupt enable
\      21 CONSTANT: RXRL     \ receive register loaded bit
\      22 CONSTANT: TXRE     \ transmit register empty bit
\      27 CONSTANT: -DSR     \ NOT data set ready
\      28 CONSTANT: -CTS     \ NOT clear to send
       31 EQU RESET          \ 9902 reset bit

[CC] HEX
       7F EQU $7F

TARGET-COMPILING
VARIABLE: BPS    0034 BPS T!   \ 9600 baud
VARIABLE: PROTO  9300 PROTO T! \ 8 bits, no parity, 1 stops
VARIABLE: PORT

CROSS-ASSEMBLING
l: LEDON
         R12 RS232/1 LI,    \ select the card
         7 SBO,             \ turn LED on
         RT,
l: LEDOFF
         R12 RS232/1 LI,    \ select the card
         7 SBZ,             \ turn LED off
         RT,

CODE: OPEN-TTY  ( port -- ) \ Usage: /TTY1 OPEN-TTY
         R12 RPUSH,
         LEDON @@ BL,
         TOS R12 MOV,       \ load 9902 port address
         RESET SBO,         \ reset card
         TOS PORT @@ MOV,   \ set the port variable to use
         PROTO @@ 8 LDCR,   \ set protocol
         LDIR SBZ,          \ disable 9902 timer
         BPS @@ 0C LDCR,    \ set baud rate
         TOS POP,           \ refill TOS
         LEDOFF @@ BL,
         R12 RPOP,          \ restore R12
         NEXT,
         END-CODE

[CC] DECIMAL
CROSS-ASSEMBLING
\ this word turns on the LED when sending
CODE: (CEMIT) ( c -- )
         R12 RPUSH,
         LEDON @@ BL,
         PORT @@ R12 MOV,
         BEGIN,
           27 TB, EQ    \ test -DSR bit =0
         UNTIL,
         16 SBO,        \ set RTS
          BEGIN,
           22 TB, EQ    \ wait XBRE empty
         UNTIL,
         TOS SWPB,      \ put byte on the other side
         TOS 8 LDCR,    \ send 8 bits
         16 SBZ,        \ reset RTS
         OUT  @@ INC,
         VCOL @@ INC,
         TOS POP,
         LEDOFF @@ BL,
         R12 RPOP,
         NEXT,
         END-CODE

CODE: CKEY? ( -- n )      \ fast tty read
         R12 RPUSH,
         PORT @@ R12 MOV,    \ select the 9902
         TOS PUSH,
         TOS CLR,
         21 TB,              \ test if char ready
         EQ IF,
            TOS 8 STCR,      \ read the char
            TOS 8 SRL,       \ shift to other byte
               18 SBZ,       \ reset char buffer
         ENDIF,
         R12 RPOP,
         NEXT,
         END-CODE

 

 

 

I can send code to Forth over RS232 and compile programs but without interrupts its slow. Terra Term is set to delay each character by 1 ms and each line delays 300mS to be sure the old 99 has time to compile the line.

 

I have 2 options to fix that.

  1. Figure out how to write a Terra Term script that send text programs and waits for the echo back from Forth, and waits for 'ok' back from Forth after a newline.
  2. Use the interesting interrupt stealing method shown in tech pages and force the old machine to keep up.

I have also had a change of heart on adding the final ANS Forth words to the system on startup with a floppy disk.

I have changed from loading each small file one a time to putting all the definitions in a new file called "SYSTEM". This makes the system start much faster.

 

The Video below shows the system booting from floppy disk and loading SYSTEM and the VT100 terminal control library.

 

It's kind of fun having two screens on the TI-99. I ran a demo program that uses Graphics mode 1 to produce color bars .

Graphics on the old CRT and text interpreter control on the PC.

 

It's what I wanted 30 years ago...

 

 

 

COM1 - Tera Term for TI-99 VT 2_10_2019 4_23_41 PM.mp4

  • Like 2
Link to comment
Share on other sites

VT100 Terminal Control in Forth

Somebody somewhere might have a use for this code. I chose to create a kind of markup language to do this job.
The last line is for "vectored execution" . In your system you would need the following to make It work.
xx is the number of your system's USER variable.

NOTE: You can use a regular variable in a single threaded system


xx USER 'PAGE  \ variable that hold execution address

: PAGE   'PAGE @ EXECUTE ;   \ get routine's address and do it

 

CR .( VT100 terminal control)
DECIMAL
\ type a number in base 10, with no space
: <ARG>   ( n -- )
           BASE @ >R  
           0 <#  DECIMAL #S  #> TYPE
           R> BASE ! ;

\ markup language for terminal control codes
: <ESC>[  ( -- )   27 EMIT  91 EMIT  ;
: <UP>    ( n -- ) <ESC>[ <ARG> ." A" ;
: <DOWN>  ( n -- ) <ESC>[ <ARG> ." B" ;
: <RIGHT> ( n -- ) <ESC>[ <ARG> ." C" ;
: <BACK>  ( n -- ) <ESC>[ <ARG> ." D" ;
: <HOME>  ( -- )   <ESC>[ ." 0;0H" ;
: <CLS>   ( n -- ) <ESC>[ ." 2J" ;
: <CLRLN> ( -- )   <ESC>[ ." K" ;

\ redefine Forth words using markup words
: AT-XY   ( col row --) 
          2DUP VROW 2!
          SWAP <ESC>[ <ARG> ." ;" <ARG> ." f" ;

: TTYPAGE ( -- ) <CLS>  <HOME> ;

CR .( Set PAGE vector for VT100)
' TTYPAGE  'PAGE !

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

I am having fun using my new terminal interface to 99. It will be better once I get the rcv side running with an interrupt but it is still useful as is.

 

I have just discovered the game bar recorder on Windows 10 so now I can add a little commentary to my videos. Turn off the sound if it is too painful to listen to. :-)

 

It's time to create a youtube channel for these things I guess.

 

https://www.youtube.com/watch?v=Ao0stVFFl3Q

 

 

BTW, did you notice that we built some assembly language code from the console and could use it immediately?

Interactive Assembly language is a Forth super power IMHO.

 

Edit: added the code here

\ simple ISR counter example

NEEDS DUMP FROM DSK1.TOOLS
NEEDS MOV, FROM DSK1.ASM9900

VARIABLE X
CODE COUNTER ( -- ) \ example ISR
     X @@ INC,
     RT,
     ENDCODE

: ?CODE ( cfa -- ) DUP @ 2- - ABORT" Not code word" ;

\ API LAYER
: ISR'  ( -- code-address)
        BL WORD  FIND  0= ABORT" ISR not found"
        DUP ?CODE >BODY ;

: INSTALL   83C4 ! ;   \ Usage: ISR' COUNTER  INSTALL
Edited by TheBF
  • Like 2
Link to comment
Share on other sites

And while we have a way to hook the interrupt, how about a 32 bit timer.

This can count to 4,294,967,296 ticks before rolling over. That's 19,884 hours or 2.26 years! :-)

Never left my old TI-99 running that long.

CREATE TIME32 0 , 0 ,

CODE DOUBLECOUNT
         TIME32 CELL+ @@ INC,
         OC IF,
              TIME32 @@ INC,
         ENDIF,
         RT,
         ENDCODE
\ to see the time value use: TIME32 2@ DU. 
  • Like 2
Link to comment
Share on other sites

And while we have a way to hook the interrupt, how about a 32 bit timer.

This can count to 4,294,967,296 ticks before rolling over. That's 19,884 hours or 2.26 years! :-)

Never left my old TI-99 running that long.

CREATE TIME32 0 , 0 ,
CODE DOUBLECOUNT
         TIME32 CELL+ @@ INC,
         OC IF,
              TIME32 @@ INC,
         ENDIF,
         RT,
         ENDCODE
\ to see the time value use: TIME32 2@ DU. 

That is really tempting to add to the fbForth 2.0 ISR, but alas, I fear I have already crammed too much code into that ISR.

 

...lee

  • Like 1
Link to comment
Share on other sites

 

And while we have a way to hook the interrupt, how about a 32 bit timer.

This can count to 4,294,967,296 ticks before rolling over. That's 19,884 hours or 2.26 years! :-)

Never left my old TI-99 running that long.

CREATE TIME32 0 , 0 ,
CODE DOUBLECOUNT
         TIME32 CELL+ @@ INC,
         OC IF,
              TIME32 @@ INC,
         ENDIF,
         RT,
         ENDCODE
\ to see the time value use: TIME32 2@ DU. 

That is really tempting to add to the fbForth 2.0 ISR, but alas, I fear I have already crammed too much code into that ISR.

 

...lee

 

I was not aware of that.

 

What have you got running on it now?

Link to comment
Share on other sites

I was not aware of that.

 

What have you got running on it now?

 

I suppose it is not all that much when there is no speech or sound but here it is for your delectation:

 

 

 

*     __                  __                __
*    / /  ___ _    ______/ /  ___ _  _____ / /
*   / /__/ _ \ |/|/ /___/ /__/ -_) |/ / -_) / 
*  /____/\___/__,__/   /____/\__/|___/\__/_/  
*                     ____                        __ 
*                    / __/_ _____  ___  ___  ____/ /_
*                   _\ \/ // / _ \/ _ \/ _ \/ __/ __/
*                  /___/\_,_/ .__/ .__/\___/_/  \__/ 
*                          /_/  /_/                  
* 
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*                                                                   *
* fbForth---                                                        *
*                                                                   *
*       Low-level support routines                                  *
*                                                                   *
*  << Including Trampoline Code, tables & variables:  2606 bytes >> *
*                                                                   *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

LLVSPT   ; <--This is the source copy location for the rest of this code.

$BUFF  EQU  >2010
 
* 4 I/O buffers below ($LO = >3020) 
* Change '4' to number of buffers needed and for which there is room.

$LO    EQU  4*>404+$BUFF      start of low-level routines after I/O buffers
*       _____   ____         __  __      ___________ 
*      / _/ /  / __/__  ____/ /_/ /     /  _/ __/ _ \
*     / _/ _ \/ _// _ \/ __/ __/ _ \   _/ /_\ \/ , _/
*    /_//_.__/_/  \___/_/  \__/_//_/  /___/___/_/|_| 
*
;[*** Interrupt Service =======================================================
* This routine is executed for every interrupt.  It processes any pending
* speech and souind.  It then looks to see whether a user ISR is installed in 
* ISR.  If so, it sets up NEXT for execution of the user ISR.  This will work 
* only if the user has installed an ISR using the following steps in the fol-
* lowing order:
*
*   (1) Write an ISR with entry point, say MYISR.
*   (2) Determine code field address of MYISR with this high-level Forth:
*           ' MYISR CFA
* <<< Maybe need a word to do #3 >>>
*   (3) Write CFA of MYISR into user variable ISR.
*
* Steps (2)-(3) in high-level Forth are shown below:
*           ' MYISR CFA
*           ISR !
* 
* <<< Perhaps last step above should be by a word that disables interrupts >>>
*
* The console ISR branches to the contents of >83C4 because it is non-zero,
* with the address, INT1, of the fbForth ISR entry point below (also, the
* contents of INTLNK).  This means that the console ISR will branch to INT1
* with BL *R12 from WP = GPLWS (>83E0), R12 containing INT1 below to first
* process any pending speech and sound.
* 
* If the user's ISR is properly installed, the code that processes the user
* ISR modifies NEXT so that the very next time B *NEXT or B *R15 is executed
* from Forth's workspace (MAINWS), the code at INT2 will process the user's
* ISR just before branching to the normal NEXT entry ($NEXT) in fbForth's
* inner interpreter.
*** ==========================================================================

* ¡¡¡ MUST REMEMBER THAT WE ARE IN GPL WORKSPACE UPON ENTRY. !!!

INT1   EQU  $LO+$-LLVSPT
       LI   R0,BRSTK          load address of top of Branch Address Stack
* 
* Set up for pending speech
*
       MOV  @SPCSVC,*R0       save Speech service address onto Branch Stack
       JEQ  SNDCH1            jump to sound-check if no speech
       INCT R0                increment Branch Stack
*
* Set up for pending sound table #1 (ST#1)
*
SNDCH1 MOV  @SND1ST,R2        sound table ST#1 to service?
       JEQ  SNDCH2            process speech and sound if needed
       LI   R1,PLAYT1         load PLAYT1 address and...
       MOV  R1,*R0+           ...push it onto Branch Stack
* 
* Set up for pending sound table #2 (ST#2)
*
SNDCH2 MOV  @SND2ST,R3        sound table ST#2 to service?
       JEQ  PRCSPS            process speech and sound if needed
       LI   R1,PLAYT2         load PLAYT2 address and...
       MOV  R1,*R0+           ...push it onto Branch Stack
*
* Process sound stack if both sound tables idle
*
PRCSPS SOC  R2,R3             OR R2 and R3..both sound tables idle?
       JNE  PRSPS2            nope..skip sound stack processing
       LWPI SND1WS            switch to ST#1 WS
       CI   R4,SNDST0         anything on sound stack?
       JEQ  PRSPS1            no..exit sound stack processing
       DECT R4                pop sound stack position
       MOV  *R4,R2            get sound table address from sound stack
       INC  R0                kick off sound processing of ST#1 (R0=1)
PRSPS1 LWPI GPLWS             switch back to GPL WS
* 
* Check for any pending speech and sound
*
PRSPS2 CI   R0,BRSTK          any speech or sound to process?
       JEQ  USRISR            if not, jump to user ISR processing
       LI   R1,BNKRST         yup..load return address
       MOV  R1,*R0            push return address onto Branch Stack
* 
* Process pending speech and sound
*
       MOV  @MYBANK,@BANKSV   save bank at interrupt
       CLR  @>6002            switch to bank 2 for speech & sound services
       LI   R7,BRSTK          load top of Branch Stack
       MOV  *R7+,R8           pop speech/sound ISR
       B    *R8               service speech/sound
*
* Restore interrupted bank
*
BNKRST EQU  $LO+$-LLVSPT      return point for speech and sound ISRs
       MOV  @BANKSV,R0        restore bank at interrupt
       SRL  R0,13             get the bank# to correct position
       AI   R0,>6000          make it a real bank-switch address
       CLR  *R0               switch to the bank at interrupt
*
* Process User ISR if defined
*
USRISR MOV  @$ISR+$UVAR,R0     User ISR installed?
       JEQ  INTEX             
*
* Fix NEXT so that the user's ISR is processed the next time B *NEXT (B *R15)
* is executed from Forth's WS (MAINWS = >8300), which it does at the end of
* every CODE word, keyboard scan and one or two other places.
* 
       LI   R1,INT2                 Load entry point, INT2
       MOV  R1,@2*NEXT+MAINWS       Copy it to Forth's NEXT (R15)
* 
* The following 2 instructions are copies of the remainder of the console ROM's
* ISR (except that 'CLR R8' was removed because it is only needed by TI Basic)
* because we're not going back there!
* 
INTEX  LWPI >83C0             Change to console's ISR WS  
       RTWP                   Return to caller of console ISR
* 
* Branch through above-modified NEXT (R15) gets us here. NEXT will be restored
* before executing user's ISR. INT3 (cleanup routine below) will be inserted
* in address list to get us back here for cleanup after user's ISR has finished.
* User's ISR is executed at the end of this section just before INT3.
* 
INT2   EQU  $LO+$-LLVSPT
       LIMI 0                 Disable interrupts
       MOVB @>83D4,R0         Get copy of VR01
       SRL  R0,8              ...to LSB
       ORI  R0,>100           Set up for VR01
       ANDI R0,>FFDF          Clear VDP-interrupt-enable bit
       BLWP @VWTR             Turn off VDP interrupt
       LI   NEXT,$NEXT        Restore NEXT
       SETO @INTACT           Set Forth "pending interrupt" flag
       DECT R                 Set up return linkage by pushing 
       MOV  IP,*R             ...IP (R13, next Forth CFA) to return stack and
       LI   IP,INT3           ...setting IP to INT3 (below) for cleanup
       MOV  @$ISR(U),W        Do the user's Forth ISR by executing
       B    @DOEXEC           ...it through Forth's inner interpreter
* 
* Clean up and re-enable interrupts.
*
INT3   EQU  $LO+$-LLVSPT    
       DATA INT3+2            $NEXT (or $SEMIS) puts INT3+2 in W (R10)
       DATA INT3+4            DOEXEC (or $SEMIS) will branch to *W = INT3+4 (next instr)
       MOV  *R+,IP            Start cleanup: pop IP from before call to user's ISR
       CLR  @INTACT           Clear Forth "pending interrupt" flag
       MOVB @>83D4,R0         Prepare to restore VR01 by...
       SRL  R0,8              ...moving payload to LSB (enabling VDP interrupt) and
       AI   R0,>100           ...VR # (01) to MSB
       MOVB @VDPSTA,R1        Remove pending VDP interrupt by reading VDP status
       BLWP @VWTR             Write VR01
       LIMI 2                 Re-enable interrupts
       B    *NEXT             Continue normal task

 

 

 

There are three entry points, so all of the code is not executed at every interrupt, but still ...

 

...lee

Link to comment
Share on other sites

Ya that could be quite a few cycles worst case. However, adding that 32 bit counter would be a pretty small increase as a percentage of load.

If you decide to try it, do a double check on the cell order of 2VARIABLES in FigForth. It might be reversed to ANS. (?)

 

Topic shift.

I am considering adapting my sound control to use this ISR hook to decrementing 4 delay values for the 4 voices.

That will be a very simple way to have background sound. Set-up the freq. and level and set a delay variable for that voice. Then the interrupt counts it down to zero while the code continues.

 

With that feature I can finish my music script language and actually write polyphonic music.

 

(Working on the RS232 interrupt code. Just realized that I forgot I was in a different workspace when I wrote my idea of how to enqueue the characters. DUH!. Used R8 instead of R4. I am hoping this gets it working)

  • Like 1
Link to comment
Share on other sites

Make a Tool
Now that I am working on real hardware again it became clear that I need more tools. Tursi has spoiled me with Classic99.

One such tool is a DUMP utility to see memory. The spoiler below shows how I wrote mine. I used the DOS debugger dump function as my example for better or worse. It works great but... the TI-99 has VDP memory and I have a fancy new SAMS card. This DUMP utility cannot cope with these.

 

What's a fella to do?

 

 

\ BFox DUMP looks like dos debug, but without segment address
HEX
2E CONSTANT '.'
3A CONSTANT ':'
 
: .####   ( n --) 0 <# # # # # #> TYPE ;
 
: .ASCII  ( adr n --)
          BOUNDS
          DO
            I C@ DUP
            BL 1-  80 WITHIN
            0= IF DROP  '.'
            THEN EMIT
          LOOP ;
 
DECIMAL
: ?80    ( -- 16 |   \ test for 80 or 40 column mode
          VMODE @ 80 = IF 16 ELSE 8 THEN ;
 
.( ..)
HEX
: DUMP   ( offset n -- )
         BASE @ >R
         HEX
         BOUNDS   ( -- endadr startadr)
         DO PAUSE
         CR  I  .####  ':' EMIT
             I  ?80  BOUNDS DO  SPACE I @ .####  2 +LOOP SPACE
             I  ?80  .ASCII
             ?BREAK
          ?80 +LOOP
          CR
          R> BASE ! ;
 

 

 

 

Answer: DEFER

 

ANS Forth standardized a defining word called DEFER that let's you create a routine that does nothing. :-)

That is until you give it something to do as shown below.

Example:

DEFER TEST
TEST 
*Un-defined DEFER"  \ CAMEL99 DEFERRED init to this behaviour

' WORDS IS TEST 

\ now invoking TEST will run the WORDS utility showing all the system commands.

How will deferred words help fix DUMP?

If you look at DUMP you will see it uses @ (fetch an integer) and the .ASCII routine uses C@ (fetch a character)

 

I created two deferred words, MEM@ and MEMC@, and I replaced the originals with these new deferred words.

Now all I have to do is set MEM@ and MEMC@ to the correct memory operator before I run DUMP and voila! I can see all my memory spaces (well except GROM, but I could do that too if I wanted to)

 

Here is the additional code that does the job:

DEFER MEM@
DEFER MEMC@

: DUMP    ['] @  IS MEM@    ['] C@  IS MEMC@  (DUMP) ;
: VDUMP   ['] V@ IS MEM@    ['] VC@ IS MEMC@  (DUMP) ;
: SDUMP   ['] @P IS MEM@    ['] C@P IS MEMC@  (DUMP) ;

Note: (DUMP) is the renamed DUMP code.

The brackets are Forth naming convention meaning that this is not to be used as is but is a support routine.

 

You can see in each DUMP word that we lookup the execution of address of each memory operator with [']

Then we use IS to assign it to the DEFERRED word. Then we run (DUMP).

 

I'm back in business... until next time I hit a stumbling block.

 

 

 

 

post-50750-0-58710000-1550804494.jpg

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