Different/best ways to communicate with I/O (serial/vga/lcd/keyboard) driver
Cluso99
Posts: 18,069
in Propeller 2
Premise: The actual driver is in its' own cog (cpu core) most likely running pasm (assembler).
So, what is the best/easiest way to interface to this/these drivers from a user program (spin, pasm, c, basic, python, or other) running in a different cog?
Can we substitute the driver without changing the user program? Hopefully even without re-compiling the program???
There are a couple of solutions carried over from P1...
FullDuplexSerial (FDX) uses a common hub buffer, typically of 16 bytes, with a head pointer and a tail pointer although it's more of an offset than a pointer. There are two sets in FDX, one for transmit and one for receive. The buffer starts as empty where both the head and tail are "0". As the master (the user program for transmit, and the driver for receive) puts a character into the buffer, it increments the head. The slave (the driver for transmit, and the user program for the receive) waits for the head and tail to be different (indicates a character in the buffer), and then reads the character from the buffer at the tail (offset), increments the tail, and if it exceeds the buffer length it is reset to zero.
This is a simple mechanism where no real processing of the characters is done within the driver, meaning no character functions/manipulations/expansions (eg string. hex, decimal, etc) are done within the driver. Depending on the driver, control character interpretation could be performed within the driver.
Any character functions/manipulations/expansions (eg string. hex, decimal, etc) were done by the user program calling a spin interface (and code) residing within the driver's code, but not within its' cog pasm code. This meant that the call had to be made from spin, meaning the users program had to also be in spin, and compiled as part of the users spin program. This meant a different mechanism, and code and driver, was necessary for other programming languages, such as C and basic.
VGA/LCD/Video drivers typically used a different mechanism where the driver typically consisted of spin calls, and in spin, and compiled as part of the users spin program. This spin code typically handled any screen clearing, scrolling, etc. The screen buffer was in hub and was manipulated by these spin routines. The pasm driver cog merely rendered the video output to the VGA/LCD/Video hardware from the hub screen buffer, rendering with a font where necessary.
PS2 Keyboard - I think it used a buffer with head and tail like FDX. I'm not sure and haven't checked. I do know the pasm cog driver section did the conversion to characters from the keypresses it received.
Problems - These differing methods, as well as the fact that the spin interfaces were named differently (eg OUT vs TX etc) meant substituting one driver for another was complex. Adding to that, the main compiler (PropTool) did not permit condition compilation, so you just couldn't write hardware independent code easily. Luckily for us, Brad and Mark produced bst and homespun although its' use wasn't as widespread as it could have been.
Dracula wrote some code that could load "coglets" which permitted some driver substitution but other than his own use it didn't go anywhere. I wrote my Propeller OS that could compile using either FDX or 1-pin TV and 1-pin Keyboard, using an indirection interface. Again, there was little use outside my own.
What do we have for P2 now?
JonnyMac has written an FDX equivalent for P2 jm_fullduplexserial.spin2 together with a string handler jm_nstr.spin2. His fdx uses the buffer with head and tail mechanism.
I wrote the serial/monitor/debugger in the P2 ROM. It uses hubexec calls and 16 registers in the user program. This can be run from any language, provided you can reserve the 16 cog register space. I have re-written this to be reloadable pasm hubexec code which can again be called from any language provided you can reserve the 16 cog registers. I'm sure (with overheads) I could remove this constraint.
It has all the inbuild functions string, hex, dump (a line of memory dump including ascii), read, readline, etc.
I started making this a standalone pasm cog driver but abandoned this as I'm unconvinced anyone will use it beside myself.
Where to from here?
Do we just go down the same old P1 path where you need to modify your program and create a different version?
Or do we want to get smarter and use some hardware independence?
And if yes, how?
So, what is the best/easiest way to interface to this/these drivers from a user program (spin, pasm, c, basic, python, or other) running in a different cog?
Can we substitute the driver without changing the user program? Hopefully even without re-compiling the program???
There are a couple of solutions carried over from P1...
FullDuplexSerial (FDX) uses a common hub buffer, typically of 16 bytes, with a head pointer and a tail pointer although it's more of an offset than a pointer. There are two sets in FDX, one for transmit and one for receive. The buffer starts as empty where both the head and tail are "0". As the master (the user program for transmit, and the driver for receive) puts a character into the buffer, it increments the head. The slave (the driver for transmit, and the user program for the receive) waits for the head and tail to be different (indicates a character in the buffer), and then reads the character from the buffer at the tail (offset), increments the tail, and if it exceeds the buffer length it is reset to zero.
This is a simple mechanism where no real processing of the characters is done within the driver, meaning no character functions/manipulations/expansions (eg string. hex, decimal, etc) are done within the driver. Depending on the driver, control character interpretation could be performed within the driver.
Any character functions/manipulations/expansions (eg string. hex, decimal, etc) were done by the user program calling a spin interface (and code) residing within the driver's code, but not within its' cog pasm code. This meant that the call had to be made from spin, meaning the users program had to also be in spin, and compiled as part of the users spin program. This meant a different mechanism, and code and driver, was necessary for other programming languages, such as C and basic.
VGA/LCD/Video drivers typically used a different mechanism where the driver typically consisted of spin calls, and in spin, and compiled as part of the users spin program. This spin code typically handled any screen clearing, scrolling, etc. The screen buffer was in hub and was manipulated by these spin routines. The pasm driver cog merely rendered the video output to the VGA/LCD/Video hardware from the hub screen buffer, rendering with a font where necessary.
PS2 Keyboard - I think it used a buffer with head and tail like FDX. I'm not sure and haven't checked. I do know the pasm cog driver section did the conversion to characters from the keypresses it received.
Problems - These differing methods, as well as the fact that the spin interfaces were named differently (eg OUT vs TX etc) meant substituting one driver for another was complex. Adding to that, the main compiler (PropTool) did not permit condition compilation, so you just couldn't write hardware independent code easily. Luckily for us, Brad and Mark produced bst and homespun although its' use wasn't as widespread as it could have been.
Dracula wrote some code that could load "coglets" which permitted some driver substitution but other than his own use it didn't go anywhere. I wrote my Propeller OS that could compile using either FDX or 1-pin TV and 1-pin Keyboard, using an indirection interface. Again, there was little use outside my own.
What do we have for P2 now?
JonnyMac has written an FDX equivalent for P2 jm_fullduplexserial.spin2 together with a string handler jm_nstr.spin2. His fdx uses the buffer with head and tail mechanism.
I wrote the serial/monitor/debugger in the P2 ROM. It uses hubexec calls and 16 registers in the user program. This can be run from any language, provided you can reserve the 16 cog register space. I have re-written this to be reloadable pasm hubexec code which can again be called from any language provided you can reserve the 16 cog registers. I'm sure (with overheads) I could remove this constraint.
It has all the inbuild functions string, hex, dump (a line of memory dump including ascii), read, readline, etc.
I started making this a standalone pasm cog driver but abandoned this as I'm unconvinced anyone will use it beside myself.
Where to from here?
Do we just go down the same old P1 path where you need to modify your program and create a different version?
Or do we want to get smarter and use some hardware independence?
And if yes, how?
Comments
In Tachyon/TAQOZ there are two vectors "uemit" and "ukey" and words like KEYB and VGA and CON and File I/O will simply change these vectors. My "app" code just outputs or inputs from any of these etc so if I were to dump memory for instance I could redirect to a file instead and then back to the console.
That's my 2cents. Do you have change?
This is my _STDIN spin object and my _STDOUT spin object
It is a single character hub mailbox each way, so the driver must do any required buffering tho there is usually plenty of time to do this, and peel it off as required. Sorry, I only work/round with 5c minimum
So I think for simple terminal I/O, the mailbox approach holds worth: Compatible with any language, pushes the buffering onto the drivers, pushes the formatting into the application. Just need to find some standard for these...
Here's my arbitrary proposal:
- word[$7BFF0] for input mailbox, word[$7BFF2] for output mailbox. entire area from $7BFF0 to $7BFFF is reserved for future use.
- All characters get taken +1 when placed in a mailbox to be able to pass $00.
- Characters greater than $FF are reserved for future use and must be ignored by output drivers when emitted by the application and ignored by the application when emitted from an input driver.
- Input drivers must not emit carriage returns, only line feeds
- Applications should not emit carriage returns, output drivers should ignore them.
- output drivers should support PST-style control codes (because easy to implement). 0 may be interpreted as "clear screen", but applications should use 16 for that.
- output drivers may or may not support ANSI escape codes.
- input drivers may report cursor movement using 3 for left, 4 for right, 5 for up and 6 for down. All other non-printable characters are to be ignored by applications.
This doesn't really cover where the drivers may keep their larger buffers. OTOH, there's 2K LUT RAM per cog and always reserving more than 2K hub would be wasteful, anyways. 2K is enough for an 80 column display, so it's probably fine.