A description of the CP/M 2.2 IOByte.
|
|
PUTTING THE CP/M IOBYTE TO WORK
Among the other marvels of CP/M is a single byte, 8 bits, located at address 3 in the standard version.
This is known as the "IOBYTE
."
You may not have given this much thought if you have a rather straightforward, factory-configured system;
if you do your own interfacing, though, it can be very useful.
It is the means by which you can reconfigure your system from the keyboard.
You might want someone at the other end of your modem to operate your system, for example, or you may want to be able to switch printers without any hassle.
CP/M assumes that you have four logical peripherals called Console, Tape punch, Tape reader, and List device.
Control normally takes place from the console (CON
) and printing is automatically routed to the list device (LST
).
In the good old days everyone had a paper tape punch and reader, we are lead to believe.
Nowadays it is more common to find a video terminal and a single printer of some kind.
But what if you have more than two peripherals or even more than those four basic devices?
Or what if you have two printers? Or two terminals?
(Some people do.)
Perhaps, too, you have a modem or some other device for input and/or output.
You can have these extras by various means;
the IOBYTE
is one way to go about it.
The other methods are hard-wiring, changing cables, or using an expensive switching arrangement.
What the IOBYTE Does
Let's assume that you have a CP/M based computer at hand.
You're probably familiar with the use of the STAT
command to check disk space and file lengths.
You may not have tried the STAT DEV:
command.
If you will enter that one you will get something like the following:
CON is TTY
RDR is TTY
PUN is TTY
LST is TTY
All this really says is that there is a in the IOBYTE
.
This is the default value in a completely naked CP/M.
The system simply read address three and, finding each bit a zero, concluded that all peripherals were TTY
.
It didn't care what they actually were, it simply described the configuration in its own terms. Looking back a few years this makes sense, because not too long ago a TTY
(Teletype) was about the only I/O device available to hackers.
All of your peripherals (if you had more than one) were probably TTY
s. Actually many teletypes did have a built in punch and reader as well as the printer and keyboard, so they could be any of the four logical I/O devices.
Many CP/M systems default to a 95 hex in the IOBYTE
, which will yield:
CON is CRT
RDR is PTR
PUN is PTP
LST is LPT
By entering the commend STAT VAL: you get something like this:
CON:TTY CRT: BAT: UC1
RDR:TTY PTR: UR1: UR2
PUN:TTY PTP: UP1: UP2
LST:TTY CRT: LPT: UL1
Which means that the CON
(sole) could have any of the four assignments listed on the same line;
any of four devices could be the RDR
, and so on.
Notice that some of the possible device names are input devices, some are output, and some (like the TTY
) are capable of both.
Thus there are some limits to the reassigning you can do;
you can't, for example, 'read' input from a printer!
The BAT
(batch) function is really not a device as such;
it is a way of routing continous input from one device to another and has to be handled a bit differently than would a more conventional peripheral operation.
So although it looks like 16 devices can
be accomodated, this is not really true.
Now try the command STAT CON:UC1:
Entering STAT DEV:
will now tell you that CON
is UC1
rather than the CRT
or BAT
TTY that was its previous designation.
It does this because the previous command changed the value of the IOBYTE
.
If you change the assignment of the LST:
device by means of another STAT
command, the number changes again and the result of STAT DEV:
changes accordingly.
But unless the IOBYTE
has been implemented in your system nothing else will happen.
The only 'real' connection between the logical I/O devices and the actual peripherals has to take place in software.
This is sometimes referred to as linking the logical and physical devices, and the IOBYTE
is the way it happens
How It Works
To follow the rest of this article you should have some familiarity with assembly language programming and CP/M;
not a great deal, but just enough to find and modify your driver routines, which are a part of the BIOS.
If you have ever modified or customized your CP/M system you will be familiar with how these steps are performed.
If not, it's a good exercise for learning about your operating system and the details are very likely buried somewhere in your documentation.
First, take the IOBYTE
apart.
If you'd like, you can use DDT
to examine the contents of address 3 which will be the hex number stored in the IOBYTE
.
The 8 bits are divided into 4 2-bit groups, one group for each logical device.
It really makes more sense to consider the byte as four separate '2-bit bytes.'
Ones and zeros in a 2-bit format can represent four values – through 3.
A zero in any group means that device has been assigned as a TTY
for purposes of the STAT
commands.
A zero in the CON
group (the two least bits in the byte) means TTY
, a one means CRT
, and so on.
In the highest bits, taken separately, zero again means TTY
and one means CRT
, two means LPT
, and three some other device.
Figure 1 shows how the byte is divided in four portions for this purpose.
If we put a two in the high group and a one in the low group and then read the byte as one number, it will be 81 hex, and it will tell us (via STAT
) that we have a CRT
as console and an LPT as the list device.
The other two devices are TTY
because their groups contain all zeroes.
If this is not clear to you, read the last few paragraphs again and (as a last resort) consult your CP/M documentaion.
But still, nothing happens unless our driver routines can read the IOBYTE
and find out where to go for input or output, as the case may be.
Actually, most CP/M systems will default (unless they've been modified) to either a zero or 95 hex in the IOBYTE
.
Zero says everything is TTY
;
95 makes some other assignment.
If you want something else, there are ways to change it;
the method we prefer is to make the byte you want load as part of your initialization routine, on cold boot.
It should load with a value that configures your system as you normally use it; you can then make other assignments with the STAT
commands.
Another fairly practical way of manipulating the byte –
reading it and/or changing it –
is from a running program in BASIC or whatever language you have, that can "peek and poke."
At this point we might want to refer to a flowchart or two.
Figure 2 is the flowchart of a driver routine which ignores the
IOBYTE
and simply routes console calls to the terminal and print calls to a single printer.
Figure 3 shows a driver which will consider the
IOBYTE
and route the I/O accordingly.
We'll assume that your initialization routine (another jump in the jump table) has also been written to
insert some significant number in the
IOBYTE
or that you have arranged to live with what your system already defaults to.
In this case we'll assume that we are dealing only with the print output.
At the beginning of the so-called "user area" in the BIOS there is a jump table.
A call to the printer, for example, is routed to the
LST
vector, which in turn is a jump to the routine that actually outputs the character. In
Figure 2 we have shown the call going straight from the jump table to the driver routine.
In
Figure 3, we show the printer call being routed through another logic block, which reads the
IOBYTE
and, based on what it finds there, re-routes the information to any of two or more printers.
Once this routine is in place, we can choose our printer simply by changing the
IOBYTE
from the keyboard.
We would do this by
STAT LST:TTY:
, or
STAT LST:LPT:
, or whatever.
The same sort of thing has to be done for each logical device (LST
, CON
, RDR
, or PUN
) to which we want to add this versatility.
RDR
and PUN
, incidentally, don't have to be paper tape devices.
The punch might be another printer, or RDR
and PUN
might be the input and output to and from a modem.
Our publisher, who is interested in using things other than terminals and printers with a computer, has pointed out that some of the possible devices might be sensors or control systems of some kind.
That's entirely possible and practical.
You might want to boot up in the normal operating mode and at some point switch your system to monitor or control some kind of external process.
Another good purpose could be accomplished by putting your equipment in the basement or closet, with an extension terminal in the living room that you could move to once the system is running.
In a business the first printer might be a high speed dot matrix and the second a daisy wheel for word processing.
The first console might be on the secretary's desk, and the second in the boss's office.
Note that this would constitute a very cheap, simple multi-user system for two users.
It actually only allows for one user at a time, but still does accomodate two users in some fashion.
The Hardware/Software Changes
In order to do the things just discussed you need a port (serial or parallel as the case(s) might be) for each device to be connected.
With an S-100 system you can plug in additional I/O boards until you run out of slots;
some other computers might be more limited as to he number and type of available ports.
That's the hardware, and of course each
port has to be configured according to the documentation of the port itself and the device to be connected.
This means setting the baud rate (for serial ports) and the status bits, and all that good stuff.
Once those things are established you will have to (carefully) modify or rewrite the driver routines in your BIOS.
That's the software or fun part of the job, and the part we'll discuss next.
Assume that we are dealing only with the printer or LST output for now and that you have a copy of the source listing for the user portion of the BIOS.
1. Locate the jump table.
There will be a jump instruction which directs printed output to the print driver routine.
This instruction will contain the address of the present printer driver.
2. Insert, in place of the driver, a routine to read and test the IOBYTE
.
One way to do this is to use the logical AND
function to mask and look at only the appropriate portion of the byte;
there are other ways that you might rather use.
There are styles in programming and yours might be to rotate the byte through the accumulator or "line up" the bits you're checking.
3. Depending on the value of bits 7 and 6 in the IOBYTE
, redirect the print output to one of (up to 4) new print routines.
4. At those addresses, put in the appropriate code for the port that will be used for each.
To do this you will have to know the port address, ready bits, and so on, for each.
They might all be the same or they might differ, depending on your hardware.
5. Reassemble the user portion of the BIOS and reinsert it into the CP/M system.
Perform the same steps for console or other devices that you might want to be able to reassign by use of the STAT
commands.
Always go directly from the jump table to a test routine, and from there to the appropriate driver.
Needless to say, when CP/M is implemented on a machine for which it was not designed, such as Apple or TRS-80, there will be some subtle differences.
The object, of course, is to make the machine look the same as any other to CP/M application programs;
to accomodate the foreign system, though, some compromises are usually made.
For example the Apple has no ports per se.
The logical device drivers, then, must be caused to address expansion slots, or more likely, the memory addresses assigned to them.
The peripheral cards are geared to 6502 conventions, i.e., input and output are by means of the load and store instructions rather than the IN
and OUT
instructions we 8080/Z80 people are used to.
The nominal console (the keyboard and screen), have to be taken into account;
some of the slots are dedicated and others should be available for reassignment.
I am no Apple expert and welcome correction from readers if the following is in error.
As nearly as I can tell the Apple defaults to the "normal" console configuration unless an 80 column card is plugged into slot three.
Slot three is nominally the "alternate console" and some magic in the system causes the machine to detect this addition and make the necessary patches to the drivers.
This takes place automatically and does not change the IOBYTE
.
Slot 1 is normally considered the LST
device, or printer.
An apparent difference here is that the Apple LST
assignment seems able to handle both input and output rather than just output as is the normal case.
There is also a persistent rumor that some of the available peripheral cards make direct calls to the 6502 ROM routines, as well as making other adjustments that are card-dependent.
In other words Apple CP/M is based on the original version, but has some peculiar differences.
An unaltered Apple CP/M system (at least the one I looked at) has the IOBYTE
filled with 95 hex, which presumably makes slot 1 the printer.
Since there is an 80 column card occupying slot 3 it should be possible, somehow, to assign slot 2 to be another external device, and perhaps also one or more slots at 4 and above.
The IOBYTE
does not appear to be implemented in the one I examined, and I expect that some new driver routines would have to be written before it could be put to use.
Without any more knowledge of the subject I can only advise care in re-assigning values to the Apple IOBYTE
.
It appears that the Microsoft documentation does go into some detail as to just what alterations you can and cannot make including the addresses for making alterations to the drivers.
Have fun.
For some reason I have the feeling that the TRS-80 versions of CP/M are even stranger than the Apple's.
Nuff said about that.
Listing my own setup would serve no purpose. I have, as do many hackers, a weird mixture of mis-matched equipment (basically vintage S-100) that suits me just fine, and will until I decide something has to be changed or added.
I will say that I have two console options, two printer options, a modem, and one each parallel and printer ports that are as yet not assigned to any practical purpose.
These will no doubt be my "reader and punch" someday.
I feel that being able to change these assignments from
the keyboard is a worthwhile feature. Regardless of what is
available to you, it can all be put to work by the proper
juggling of the
IOBYTE
– and in the process you get a little
closer to the heart and soul of CP/M.
Adapted for HTML by
Werner Cirsovius
December 2015
© The Computer Journal