Channels based I/O For TLC-MBC

I have settled on a final design. The 3 TTL chips are out and the GAL22V10D is in. This will free up some board space and reduce propogation delays. I am now using the 16Mhz crystal and 74LS293, that I mentioned in the last post. As I am using the WD version of the 65C02, rated at 14Mhz, I have a 4 by 2 set of pins so that I can use a jumper to select 1, 2, 4 or 8Mhz for the clock. I found another source, Mouser, for the 10.736Mhz crystals for the video, so as soon as TLC-MBC is on a PCB I can start to put the real video card together.

I have also decided to use Daryl Richter’s 65SPI chip. This will allow me to interface an SD Card, for mass storage, as well as seven other SPI devices. The GAL22V10D proved to be a pain in the proverbials! The TL886CS programmer would not program it, I tried five of them! So when I ordered the 65SPI, Daryl was kind enough to program one for me and I am now eagerly awaiting it’s arrival so that I can get back to work on TLC-MBC.

The PCF8584 I2C chip will not be on TLC-MBC, instead it will be on the I/O PCB, as it’s place has been taken by the 65SPI as that will allow me to add the SD sooner rather than later. The PCB layout for TLC-MBC has also been finished and will be sent off to Seeed Studio just as soon as I get the GAL and test it in circuit. This month has been very hardware orientated, I’ve ripped up and rearranged the layout on the Bread boards, yet again, to accomodate the above changes. With TLC-MBC waiting for the GAL, there has been no practical advancement of the software, though I have been doing a lot of research into how an OS is put together, but more of that in a future post…

Latest Update For TLC-MBC

Well, that was written a couple of weeks ago as part of another post… But things change, last week the GAL arrived and I was able to get to testing it out on Thursday. The GAL and my new breadboard layout worked first time, it was so satisfying to see the header and prompt appear on screen again after all this time without it.

The latest layout update for TLC-MBC

The latest layout update for TLC-MBC

As you can see, the three TTL chips ( two 74HC138 and one 74HC00 ) have been removed. The GAL22V10D, bottom of 2nd column from the right, replaces all three! It eats up a little more power but it is worth it for the speed, programability and conveinience. Also appearing, in the same column above the 65C02, is the 74LS293. This is used to divide the 16Mhz down into 8, 4, 2 and 1Mhz taps. Finally, in the 1st column from the right, you can see I’ve added the PCF8584. This is an I2C Interface chip. That is all the updated hardware.

Over the weekend I modified the Monitor. Previously I had a list of letters representing the commands, for example f I entered the letter ‘h’ the help screen would be displayed. If ‘h’ was third in that list then 3 would be returned as an index and that value was used in a series of comparisons to find the function that displayed the help screen. Whenever I added a command I would have to add another comparison statement. This is something that I shouldn’t have to do, the computer should do the work. So now I have a list of address of the commands and use the index number to jump to the correct address.

The second update was to change the command parser function. Previously, as mentioned above, I used single letters for each command, this led to having to be inventive with my choice of letters. For example, the copy function used the letter ‘c’, so when I added the clear screen function I used the letter ‘k’. I have updated the command parser so that now I can enter full words as well as single letters for commands. So now I can enter ‘copy’ or ‘c’ and ‘clrscr’ or ‘k’ and the parser will select the correct function. This means that I can go beyond the 26 letter limit of the alphabet as I expect that the final OS for TLC-MBC will have more than 26 command functions.

As I mentioned above, I have been doing a lot of research into Operating Systems as I would like to write one for TLC-MBC. Please note, the following is a very much simplified view of an OS and it’s functionality. My focus, at least for this post, is on channels and I/O.

The purpose of an OS is to control and allocate resources so that tasks can be efficiently executed. Regarding TLC-MBC, those resources are memory and I/O. As my OS will not be multi-tasking or multi-user, there is little need for memory management. This leaves I/O management. I/O is stuff like keyboards, screens and mass storage such as hard-drives, in other words – the hardware that is added to a computer so that it can do stuff in the real world.

For each device connected to the computer there needs to be a program called a ‘driver’ that enables the computer to read and/or write to the device. Each device has a different way of communicating with the computer, for example some use a serial protocol others use parallel with ‘hand-shaking’ and others wrap the data in packages with checksums. This makes the life of a programmer very difficult.

So how do we fix it? By unifying the method of communication between computer and device. The basic unit of communication is the character. Ideally, it would also be nice if we could have one input and one output function that dealt with all the differences between devices.


Channels are a way of doing exactly that as well as managing the resources. For example, you do not usually need every file on a drive to be open all day even if it was physically possible. You open a channel to a file, read and/or write to it and then close the channel. Terminology may be different depending on the OS, but the basic principles remain the same.

No matter what the computer, wether the humble Spectrum or a room-filling super computer, channels are used to connect the devices with the computer. There are usually at least two channels, one is for input and the other is for output. On a Unix/Linux system these are called stdin (standard input – keyboard) and stdout (standard output – screen) along with a third stderr (standard error output). As their names imply these are the standard channels that are opened when the computer starts up.

My Implementation Of Channels

This was the third update to the Monitor this weekend. I have got the basic Channel mechanism working in that when I turn on TLC-MBC, she creates the stdin, stdout and stderr channels and connects them to the correct devices. For example whenever I use the PutC function, it selects the stdout channel which is connected to the screen. So PutC will print a chr to the screen. The same for the GetC function, it looks at the stdin channel which is connected to the Serial Port which is in turn connected to the Terminal’s keyboard.

Each device connected to TLC-MBC has a list of functions that are used to communicate with it such as read a character, write a character, get the status etc. let’s call this a Device Interface. All the Device Interfaces are stored sequentially in a Device Table.

A channel is just another list in memory, it holds information such as the Device ID number and the address of the Device Interface. These channel ‘blocks’ are stored sequentially in a Channel Table. Each channel is refered to by number (0-7). Finally I have three registers in memory, stdin, stdout and stderr, these are single byte locations and hold the number of the channel they are using.

How Channels and Devices are organised and accessed

How Channels and Devices are organised and accessed

When TLC-MBC is powered-up, she creates 8 empty channel blocks in Ram. Then she ‘opens’ channel 0 by filling in information about the Serial Port into the channel 0 block and then putting a 0 in the stdin register. Next, channel 1 is ‘opened’ by filling in channel block 1 with the Screen’s info and then putting 1 in the stdout register. And so on.

The diagram to above-right shows how it is all organised (click for larger image). There are two ways to get the channel location from an STDIO register. The right way is to read the value in, for example, STDOUT, which is a 1 and then multiply that by 8 to get the Channel Offset and add that to the base address of the Channel Table. The other, slightly quicker way, is to have a second register, the DevOut Offset, this can hold either the offset 8 or even the actual address of the Channel #1 block. As I said this will speed up the access a little but at the expense of some zero page memory – as always there are trade-offs and compromises to be made depending on your goals. The extra registers have to be setup whenever the relevant STDIO register is either initialized or updated so that they stay in sync.

Once the base address of the channel is found, you then add 2 to it to get the DevOffset which is then added to the base address of the Device Table. From there we can use the offset to jump to the address of the PutC function and output the character. Sounds like a lot of code yes? Not really here it is:

   pha                            ; Save character
   ldy DEVOUT_OFFSET              ; Get channel offset
   lda CHANTABLE+CH_DEV_OFFSET,y  ; Get dev offset from channel block
   pla                            ; Restore the character
   jmp (DEVTABLE+DEV_PUTC,x)      ; and output it by the dev's putc function.

If I were to use the STDOUT register instead of the DevOut register the code would be:

   pha                            ; Save character
   lda STDOUT                     ; Get channel number
   clc                            ; Multiply by 8 to get the offset value
   asl a
   asl a
   asl a
   tay                            ; and move the offset to the Y index
   lda CHANTABLE+CH_DEV_OFFSET,y  ; Get dev offset from channel block
   pla                            ; Restore the character
   jmp (DEVTABLE+DEV_PUTC,x)      ; and output it by the dev's putc function.

As you can see there are 5 extra instructions, so the function will take longer to execute. This might not matter if the device is slow like a keyboard but if it is a high speed device that extra time might mean the difference between a successful input or losing some characters if there is no handshaking.


My implementation of channels allows for a maximum of eight channels of which three, 0, 1 and 2, are reserved for STDIO use leaving five channels for the user to define. This should be enough, but if not, I have left room for expanding the channels table by another eight. Setting up the channels functions was not easy but was definately worth the time and effort. In future when I want to use a device, once I have opened a channel for it and can use the getc, putc etc. standard functions to access it without having to worry about the data’s format or handshaking, the system will take care of it.

This functionality is working and is a huge milestone in creating a viable OS for TLC-MBC. This weekend has been very fruitful, I have:

  • Improved the way TLC-MBC finds her command functions.
  • Improved the command parser to accept both single letters and words as commands.
  • Added basic Channel based I/O functionality.

My next step is to write the command functions so that I can open and close channels to different devices from the keyboard, and to do that I have to write the drivers for the SPI and I2C chips. I also have to make a PLCC44 adapter so that I can test and write the drivers for the 65SPI. It’s close to being finished so that post will appear soon.




You may also like...