+TheBF Posted January 26, 2019 Author Share Posted January 26, 2019 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 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted January 26, 2019 Share Posted January 26, 2019 Rosetta Code Clock ... I must be missing something, but how does your code comply with rule #3 re hogging CPU time? ...lee Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 26, 2019 Author Share Posted January 26, 2019 (edited) 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 January 26, 2019 by TheBF Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted January 26, 2019 Share Posted January 26, 2019 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 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 26, 2019 Author Share Posted January 26, 2019 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. 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 26, 2019 Author Share Posted January 26, 2019 (edited) 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. 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 January 26, 2019 by TheBF Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted January 26, 2019 Share Posted January 26, 2019 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 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 29, 2019 Author Share Posted January 29, 2019 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 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 29, 2019 Author Share Posted January 29, 2019 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 - ; 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 31, 2019 Author Share Posted January 31, 2019 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. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted January 31, 2019 Share Posted January 31, 2019 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 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 31, 2019 Author Share Posted January 31, 2019 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. 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 4, 2019 Author Share Posted February 4, 2019 (edited) 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 Edited February 4, 2019 by TheBF 3 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted February 5, 2019 Share Posted February 5, 2019 ... 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 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 5, 2019 Author Share Posted February 5, 2019 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> 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. 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 6, 2019 Author Share Posted February 6, 2019 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 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 10, 2019 Author Share Posted February 10, 2019 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. 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. 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 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 11, 2019 Author Share Posted February 11, 2019 (edited) 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 February 11, 2019 by TheBF 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 13, 2019 Author Share Posted February 13, 2019 (edited) 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 February 14, 2019 by TheBF 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 14, 2019 Author Share Posted February 14, 2019 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. 2 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted February 15, 2019 Share Posted February 15, 2019 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 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 15, 2019 Author Share Posted February 15, 2019 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? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted February 15, 2019 Share Posted February 15, 2019 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 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 15, 2019 Author Share Posted February 15, 2019 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) 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 22, 2019 Author Share Posted February 22, 2019 Make a ToolNow 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. 3 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.