For quite some time I have been mulling over ways to improve character reception in a TI terminal emulator.
Most terminal emulators combine the TI interrupt service routine and the RS232's circular interrupt buffer to receive characters.
During character reception, the TI ROM ISR first determines if an external interrupt has occurred. If so, it must scan the peripheral cards one at a time to find the interrupt. Once the proper RS232 port is located, it must execute the card's interrupt service routine. The RS232's interrupt routine populates the 254 byte circular buffer that resides in VDP memory.
Unfortunately, this whole process requires a lot of overhead and starts to fail miserably at speeds above 4800bps. Not only is the buffer size too small, all of the processing required to scan the card, handle the interrupt, and stuff it into VDP, require too much time. TIMXT could not exceed 4800bps successfully - at 9600, the TI spent 100% of its time servicing the interrupts, dropping characters along the way.
Implementing hardware flow control was an option but it meant building a special cable that many people wouldn't care to make. I went through the same scenario with my Geneve terminal emulator: PORT can sustain 38.4kKbps without a special cable; it only requires handshaking when displaying color text mode (using the V9938 graphics mode and its slow character plotting) or when transferring files with Ymodem-G. But I digress.
Months later I was looking at some information on Thierry's site when I came across an interesting article. It described an ingenious method to manage external interrupts using (abusing) the ROM interrupt service routine:
This idea resonated with me. In PORT, I hijacked the system's interrupt vectors so that I could process external interrupts, video, and keyboard with no system overhead. This was "easy" to do with an OS in RAM. But the TI ROM is..well..ROM!
I was skeptical at first; will there be much of an improvement?
It took me a day or so to modify the emulator. I reserved a 4K buffer to stash incoming data. Two routines are required: an interrupt-driven RS232 capture routine and a buffer emptying routine. Think of the buffer like a bucket: one routine fills the bucket with characters; the other routine draws them out for display and other purposes. Only the active RS232 is checked and it is given immediate priority in the user ISR.
Once complete I did some base testing with my Geneve. I then asked Omega to test the program at 9600bps. He reported success. He then told me he used 19.2 with success! So, I added an option for 38.4K and surprisingly, it worked! With no hardware handshaking or cable magic!
There are a few considerations:
1. Some peripherals rely upon GPLWS R15. Jeff's method changes this value, so disk and other peripherals may fail. My solution was to modify DSRLNK to turn off RS232 interrupts and restore R15 prior to calling the ROM routine. The environment is restored afterwards.
2. All VDP-based automatic processing is inhibited. No sound, no quit key, no 50/60hz timers. You can enable the VDP interrupts and service them if you restore things to 'normal', and then re-enable the interrupt handler when complete. You cannot have both operating at the same time. I suppose a 9901-based 60hz timer may work, I'll have to try that some day.
3. Keyboard scanning takes a long time. For time-sensitive RS232 input, I use a modified direct keyboard scanning routine that can be interrupted in between steps.
My next challenges are to improve the keyscan routine and optimize the display interpreter. Later a uni-directional flow control may be needed especially when there is so much data being displayed so quickly.