Intro
Our 6502 Basic programming language implementation is quite comprehensive, but it does have some big problems. In particular, since we didn't implement the LOAD and SAVE commands when setting up basic we have to start inputting lines of code from scratch each time we start MSBASIC. Since BEN2 possesses no disk storage we cannot easily implement those commands.
A good, effective, convenient and practical workaround is to cut and paste programs from a PC editor into our terminal window. This technique is ok for a single line of code but the MSBASIC program cant process larger chunks of code arriving quickly.
The first step in solving this problem, which Ben describes, is to receive characters into a 256 byte buffer, from which BASIC can read them at a more leisurely pace.
However, when reading in larger programs the 256 byte buffer fills up and characters are dropped. To resolve this Ben implements RTS/CTS hardware flow control. When the buffer is nearly full the 6502 sets the serial pin RTS low which causes the the terminal emulator to stop sending characters. When the buffer is mostly empty the 6502 sets RTS high at which point more characters can be sent.
Ben explains how to implement these features in two videos How input buffering works and RS232 Flow Control.
Input Buffering
The original MSBASIC program uses a subroutine CHRIN to read characters in from the ACIA UART serial interface. If no character is ready CHRIN waits until a key is pressed. This works fine when a human (me) is typing on the keyboard as the program can process characters faster than I can type them.
We are going to create a buffer which will hold upto 256 characters which have been "pasted" into the terminal screen so that MSBASIC doesn't need to read them immediately. Characters are placed in the buffer as soon as they arrive and are stored in the buffer at a location pointed to by WRITE_PTR, which is then incremented. When MSBASIC needs to read a character it uses the location pointed to by READ_PTR which is then incremented. If READ_PTR=WRITE_PTR no character is available so the program waits.
We need to use an interrupt mechanism to help us with this. The ACIA UART has an interrupt (pin 26) which we connect to the Interrupt (pin 4) on the 6502. Now the 6502 interrupt pin will go high when a the ACIA has received a character.
In the 6502 software we enable interrupts and setup an interrupt vector at address 0xFFFE and 0xFFFF.
When an interrupt is received MSBASIC will jump to the interrupt vector address and run the interrupt service route to read in the character to the buffer.
On completion it will return from the interrupt to continue whatever program is running.
When MSBASIC needs to read character in we use a modified version of CHRIN. First it calls a subroutine BUFFER_SIZE to see if there are any characters in the buffer, if not it waits for characters.
If characters are available CHRIN calls READ_BUFFER to get the next character and increment READ_PTR.
This mechanism works very nicely for small chunks of data pasted in to the terminal but fails if there are more than 256 bytes waiting.
The mechanism is called a circular buffer. The WRITE_PTR variable can take values from 0 to 255. When the pointer is incremented from 255 it resets to 0 so the buffer wraps around. Similarly READ_PTR "follows" it so the buffer continues to work.
When WOZMON, the monitor, starts we initialise the buffer so READ_PTR=WRITE_PTR and the bufer is empty before we enable interrupts. We must also use the modified CHRIN subroutine to read characters in from WOZMON to the buffer.
Flow Control
The RS232 protocol specifies wires in the cable to be used for flow control, the ones we will use are CTS/RTS. On the PC end of the cable the serial driver looks at the CTS "Clear To Send" wire and if the voltage is set low the serial driver will not send any more characters until the voltage is set high. On the 6502 end the same wire is connected to RTS "Request to Send" (more appropriately "Ready for Receiving") which the 6502 will set low if it cant cope with any more characters.
Previously our serial UART only used four wires 5V, GND, RX, TX. We need to make sure our RS232 cable or FTDI connector has six wires including CTS/RTS. On the PC side the cable still plugs into a USB port. On the 6502 side we connect a BLUE wire to RTS.
Now all we have to do is update our interrupt handler so that if the buffer is nearly full we set RTS low to stop communication. When the buffer has plenty of space, set it high again. In fact the ACIA RTS pin doesn't work very well so we use PORTA PA0 instead.
Lastly we need to tell PC terminal emulation software picocom or TeraTerm that it should use hardware flow control, otherwise the terminal emulator will just keep sending data.
It works, I can cut and paste a large BASIC program (say 500 lines) into the terminal window and it will be read in correctly, although it takes a few seconds.
Outro
Input buffering and flow control are vital but not very interesting. It is pretty awesome how straightforward it is to program it on BEN2 with Bens help. By implementing both buffering and flow control we have now written a proper serial driver on our 6502.
The benefits of this work are huge. There is no way anyone would type a BASIC program in each time BEN2 was started up, but it isn't much work to spend a few seconds cutting and pasting a file before you start.
No comments:
Post a Comment