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.
Edited by TheBF, Mon Feb 4, 2019 11:20 AM.