Monday, April 22, 2019

TeleVideo Systems Part VII - Almmmost replacement for MmmOST

Almmmost CP/M file server


Functions

Almmmost is meant to be a general replacement for the TeleVideo MmmOST server software, which ran on Z80-based CP/M systems, and served data to up to 16 clients on a star-based network.  It’s designed to work on more modern hardware and uses a still-available Z85C30 serial controller chip to interface with the TeleVideo client systems.

The main functions I was looking to provide were:

  • Communications with the original hardware over an 800K baud RS-422 SDLC link
  • Compatibility with the original client OS
  • Server software that runs on hardware that’s easy to acquire, develop on, and provides a robust TCP/IP stack (and with a CPU fast enough to do TLS/SSL connections to modern systems)
  • Ability to boot the clients off of the RS-422 link
  • Private and Shared drive functionality to provide per-client storage, and the ability to share files between clients easily
  • Remote floppy drive access, replaced with images, to facilitate moving data into the system
  • Multiple client support off of one server
  • Support for standard MmmOST interfaces through RS-422 and the MULTI.SYS shared file
  • Additional support for special files, like MULTI.SYS, which provide access to dynamic data or the ability to access resources through the server’s TCP/IP connection


Not included yet, but things I’d like to add which MmmOST supported include Print queueing, file/record locking support, and communication using mailboxes/FIFOs between clients on the same server.  Printing and communication should be easy to add, but locking support will require a re-design of some of the server software.
Design

The system is built around a Next Thing Co. CHIP ARM board. It was chosen because it’s cheap and easily available (though, it is discontinued).  It should be adaptable to another Linux-based ARM board with enough GPIOs and a PWM available without too much effort.  It was designed to be modular, to make it easy to add/subtract client ports as desired.

The software is all written in C, with one shell script to set up the PWM for an 800K baud rate.  It consists of a kernel module and a single-threaded server application that uses the kernel driver to talk to clients and can interpret/process requests from clients.  Adding locking support will require moving to either a multi-threaded or state-machine model to track both client interactions and lock availability.

Hardware design


The hardware was gone through two revisions.  The first revision was a “quick and dirty” design, to glue a 5V logic Z85C30 and the RS-422 buffers to a 3.3V set of GPIOs.  It only supported a maximum of two clients (one per port on the Z85C30) but was sufficient to figure out how to interface with the clients and understand the protocol.

picture of hardware rev 1

The hardware was re-designed to use two boards, one which plugs into the ARM board’s GPIO headers, and it does 3.3V to 5V level conversion, generates the chip select lines (allowing for multiple Z85C30 chip connections), and generates the 6MHz “system” clock that is used internally by the Z85C30s.

picture of hardware rev 2

The second board handles I/O to the clients and is connected using a simple bus over a common 34-pin ribbon cable connector, allowing multiple boards to be connected easily.  The bus carries the 8 data bits, a set of chip selects to allow for multiple client-I/O boards, the 6MHz system & 800KHz Tx clock, control lines for the Z85C30s, and 5V power to the boards.

The I/O board contains a Z85C30 serial interface chip, 26LS31/32 RS-422 drivers, and a dip switch to select the board ID (which connects that chip select line to the Z85C30).

I used Eagle to design the boards and used Advanced Circuits and Seeed Studio to manufacture the boards.  I was impressed with the quality of board I could get from Seeed Studio - including fast DHL shipping I spent about $30 total to get 10 of each board.  I also got a lot better with surface mount soldering through the process.  I’ve found that I actually prefer surface mount parts to through-hole parts because they’re much easier and faster to solder as long as you have decent tools.

The board designs are linked at the end of this article in the GitHub repository.

First Gen software


My first attempt at software, I ended up trying to just use the GPIOs in userland software, and ioremap’d them into the process’s I/O space.  I was able to use this for some initial debugging and testing, but I quickly ran into a problem that Linux will steal enough time from the process to underrun or overrun the 1-byte buffer on the Z85C30.  Dropping the baud rate to 666KHz helped quite a bit, but I still had occasional corrupted packets.

This taught me a few things about how the basic I/O routines worked.  Operating SDLC on the Z85C30 was a bit different than the code on the Z80-SIO and needed me to select the correct CRC initial vector.  I was able to use the onboard Baud Rate Generator on the Z85C30, but in the end, decided that it wasn’t flexible enough, and it was easier to just generate the needed baud rate on the system PWM.

I also discovered that the routines on the TeleVideo systems transmitted the bytes in reverse order, and discarded the checksum bytes (though, they did verify that the checksum was received correctly).

Kernel Driver


After giving up on userland code to do this, I wrote a kernel driver (tvi_sdlc).  It still took some effort to make this fast enough to not underflow buffers.  I ended up having to turn off interrupts and preemption while transferring a packet two/from the Z85C30.  Fortunately, the longest packet size was 128 bytes, so the delay doesn’t cause too many problems with the system.  I also discovered that I/O to the GPIO pin memory locations (I think this really only applies to writes, but it’s possible that reads are also affected) have quite a few wait states, which limit you to about 1-2M GPIO changes per second.

With that knowledge, I cut out as many I/Os as I could, trying to do single reads/writes to change as many pins at once as possible.  This is what let me turn the baud rate back up to 800K without dropping any characters when reading/writing a port.

Overall, the driver is a fairly simple character driver, which implements IOCTL’s to change and check the state of various registers in the chip, and direct I/O to read/write packets as SDLC frames.  The application opens the device file once per I/O port it uses, and the kernel and application use that to track which port to talk to.

Application


The application is a C based program with multiple modules.  The main module (almmmost.c) reads a config file, initializes general parameters, and asks the other modules to initialize themselves.  It starts the main loop that waits for clients to make requests.  Once it receives a request, it determines where the request should be handed off to, and dispatches it to the correct modules.  The software uses a round-robin to check for requests to attempt to prevent one client from starving other clients from having requests serviced.

Once the request is received and dispatched, that request is followed through until completion.  This won’t work as written to allow for locking (as any blocking request will prevent the lock from being released), but it allowed for most of the code to be easily implemented.

The interface to the kernel module is written in almmmost_device.c.  It handles config file parsing for device configuration, resetting ports, checking the port for a request (if CTS is received), and reading/writing packets. The config file parser looks at the config file section that configures how many I/O ports should be used, and which number in the program attaches to which physical port.

The OS loading module (almmmost_osload.c) handles sending the bootloader and OS image to the client system. It’s called when the server receives a request packet with ‘L’ in the second byte and sends back the appropriate data for the system that made the request.  The module uses the config file to point at raw OS images and gives parameters that can be modified on a real MmmOST system by GENPARAM.  This data structure provides the location in RAM of the system console buffer, the print spool drive, the MmmOST “general” revision, a bitfield indicating which drives are public drives and the number of shared disks that it handles.  It then passes control to the share disk image handling module to further modify the images.

The Shared Disk Image module (almmmost_image.c) handles requests from the client to access individual 128-byte CP/M records on disks shared by the server.  This is primarily used by Private drives (which aren’t shared between clients), and Public Only drives (which don’t support shared files), but also is used for Public drives to read the disk directory by things like DIR.  It uses parameters supplied by the config file to build up the CP/M Drive Parameter Headers and Drive Parameter Blocks for the disk images.

The Shared File module (almmmost_file.c) handles requests to the shared (Public) drives. It implements a CP/M file system so that the disk is still image-compatible with CP/M.  Most functions line up with BDOS functions and handle things like opening/closing files, reading/writing files, and modifying the file’s FCB to update the current file location.  It tries to return sensible errors for various conditions, and prevent multiple clients from opening the same file to write to at the same time.  This module hands off file access to the Special file module for any files that the module will handle.

The Special File module handles files like MULTI.SYS, which don’t represent data stored on the disk image, but perform some other function. MULTI.SYS is implemented to access the system time, with other functions not yet implemented.  The module implements a simple character generator (CHARGEN.SYS), files to move data to the host filesystem directly (FILEIN.SYS and FILEOUT.SYS), a file to directly access URLs via libcurl on the host (URLGET.SYS), and two functions that use libcurl and a web service running elsewhere to convert HTML to text (LYNXGET.SYS) and to convert images to a format that can be displayed on the TS-803 and TPC-I (IMGGET.SYS).  These both use CGI scripts that are in the cgi source directory.

The Miscellaneous module (almmmost_misc.c) handles requests that didn’t fit in elsewhere.  These include print spool functions, responding to client requests for information, and the LOGON command that changes the current Private drive.

Finally, the Command line module (almmmost_cmdline.c) provides a command line for debugging and changing things on the server while it’s running, by presenting a command line when you press ctrl-C.  This lets you do things like print a list of open files, display generated disk parameters, change out disk images while the server is running, and sync disk image data/shut down the server safely.

Z80 software


Several programs were written in Z80 assembly to work with the special files.

FILEOUT copies a file to B:FILEOUT.SYS.  This was needed because PIP tries to copy to a temporary file, and then rename it, which doesn’t work correctly with special files.

IMAGEGET uses IMGGET.SYS to retrieve and display an image at an entered URL onto the screen on a TS-803 or TPC-I.

Logo displayed on a TS-803, and moon picture on a TPC-I

LYNXGET uses LYNXGET.SYS to retrieve and display a web page on the system console.

URLDISP and URLGET use URLGET.SYS get a file from a URL and display it to the console, or save it to disk, unprocessed.

URLDISP displaying HTML

Several other programs there were written at test programs to figure out how to display graphics on the systems.

Limitations


There are still some bugs in the software. It’s all single threaded, and locking will require some amount of re-writing the code to make it work.  I don’t have a real MmmOST system to compare this against, but all my test so far have produced results which at least seem reasonable to me.

Future work


Locking and print spool handling are the next two things that should be worked on.  Mailboxes/FIFOs to communicate between clients would also be nice.

One thing I noticed while working on this is that the OS images are rather large.  On the TS-803 and TPC-I, there is only about 75-80% of the system memory left for running application programs.  It’d be nice to re-write the OS to make it more efficient.  I’m not yet sure how compatible it would be, but something like TurboDOS compatibility would be nice, as it seems like a faster/better alternative to CP/M, which supported similar features to MmmOST.

Downloads

Everything can be downloaded from my GitHub repository

Thursday, April 11, 2019

TeleVideo Systems Part VI - MmmOST Protocols


TeleVideo Systems MmmOST Protocol

Methods

Reverse engineering required quite a bit of patience, and some trial-and-error. I was able to start out with schematics, a copy of MmmOST in an ImageDisk image, and along the way found a copy of the TeleVideo client source code archived online.

I still don't have access to a working Service Processor, so I haven't been able to verify that the behavior matches the original hardware, but I have been able to re-implement most of it so that the client software seems to run correctly.  I'll have a set of blog articles about that in the future, but some of the hardware that I produced for that was necessary for verifying how the protocol worked.

If you haven't read the first article with an overview of what MmmOST is and how it works, it'd be best to read through that first.

Hardware information

The systems use a Zilog Z80A SIO/2 (Z8442) chip in SDLC (aka HDLC) synchronous serial mode, with an 800KHz transmit clock derived from the 4MHz CPU clock.  This is (per specs) the fastest data rate that can be used in this mode on the SIO chip.  The system transmits over an RS-422 channel (using +/- 5V drivers into a 100ohm impedance), sending a Transmit Data (TxD), Transmit Clock (TxC), and Request to Send (RTS) signal.  It receives from the other end a Receive Data (RxD), Receive Clock (RxC), and Clear to Send (CTS) signal.  These are generated from the matching signals on the other end.


RS-422 Client connection
12345678
GNDTxD+RxD+RTS+CTS+TxC-RxC-GND
TxD-RxD-RTS-CTS-TxC+RxC+Test
9101112131415

The basic handshaking of the protocol goes like this (client and server can be reversed depending on data direction):

  1. Client raises RTS
  2. Server acknowledges and sends CTS to client
  3. Client sends data using SDLC encoding to the global address, with standard SDLC CRC-16 checksum at the end
  4. Client and server drop RTS/CTS
There are delays that are needed between 2 and 3, and after 4, to ensure that the other end is ready to receive data, and has cleared any junk out of receive buffers, and to ensure that both ends have dropped RTS after the request. These were mostly determined by trial and error.

Boot loader dump

The bootloader provided the first look at what data is sent. Requests are sent from the client to the server as a 10-byte SDLC frame, generally with the first byte (called SOR, or Start of Request) set to 01h.  The bootloader received by the firmware in EPROM as one 128-byte record, which assuming the CRC is checked out as good by the SIO chip, is then executed.   The particular machine design determines where this is loaded into RAM, but generally, it's loaded near the lowest address which is always RAM, regardless of the state of the EPROM enable latch / RAM bank switch.  On early machines, this is address 4000h, and on later machines, it's a bit above C000h.

I was able to find a copy of the boot loader in a MmmOST install floppy image, as an Intel HEX formatted file named XPD?BOOT.HEX - where the "?" varies based on machine type.

The system sends out a message, including the following:

  • Command - IPL is ASCII 'L' or 4Ch
  • USER - the hardware type -- 0 for the 801/802/800A, and other values for later machines
  • CBOOT - set to 1 since it's a cold boot; this is set to 0 if CP/M is doing a warm boot
  • RECS - the number of records that are expected for the OS image; +/- a few records will be accepted as well
  • SREC - start record number; I haven't seen this set to anything but 0
  • Signature bytes - when loading the OS image, these are always 0's
IPL request from bootloader for OS
SORIPLUSERCBOOTRECSSREC6789
80101h4Ch00h01h58h00h00h00h00h00h
802H01h4Ch01h01h68h00h00h00h00h00h
80301h4Ch04h01h6Eh00h00h00h00h00h
803H01h4Ch05h01h6Eh00h00h00h00h00h
TPC-I01h4Ch07h01h6Eh00h00h00h00h00h

Hardware capture

To get more data, I had built hardware using an ARM board (the Next Thing Co CHIP) with GPIOs interfacing to a Z85C30 serial chip. Initially, I ran this at a 666KHz clock to avoid dropping characters, but I was able to bump it up to 800KHz with some hardware and software redesign.  Any speed lower than 666KHz (which was picked as it was a divider of the clock for the Z85C30) resulted in the client system timing out.

Using this, I was able to capture that request that the EPROM was sending out for the boot loader.  It was the same 10-byte format which was used by the bootloader.  One thing that I ended up having to sort out, is that the request for the boot loader was very similar to the request for the whole OS.  The only consistent difference was that the signature bytes at the end of the request were 4,5,6,7 in the request from the EPROM (which only would process one 128-byte record) versus all zeros in the request from the bootloader, which was actually asking for the whole OS image.  The boot loader also is not a part of the OS image, so keying off of these signature bytes was critical to select what data to send to the client.

The format is the same as the above request for the OS from the boot loader.

EPROM Bootloader IPL request
SORIPLUSERCBOOTRECSSREC4567
80101h4Ch00h01h37h00h04h05h06h07h
80301h4Ch04h01hFFh00h04h05h06h07h
803H01h4Ch05h01hFFh00h04h05h06h07h
TPC-I01h4Ch07h01hFFh00h04h05h06h07h

CBIOS Source Code 

The next thing I was able to look at was a copy of the CP/M CBIOS source code, in 8080 and Z80 assembly code (the mix of the two is a pain to read - I've also discovered that Zilog Z80 mnemonics seem to make more sense and are more readable than the Intel 8080 ones).  TeleVideo actually distributed this code with machines at the time, and some of it has been preserved on Bitsavers.  The source code which I was looking at covered the TS-803 and TPC-I (which have very similar hardware) and could generate either the standalone CP/M or "USER" version for the service processor to send to clients.

Service processor requests

There are a few functions that send a request to the service processor for some non-disk related action. I call these "Checks", as they use ASCII 'C' as their function code in the request

During boot and afterward there are a few requests sent:
  • Get print spool drive
  • Start a new print spool (print the old one)
  • Get Processor ID (service processor port number) number and if autologin command should be run
  • Get MmmOST "GENREV" version
  • Directory hijack request (locking for asking to read a Public drive directory section)
Each of these is sent with a 128-byte additional record, which is unused for these requests.  The client expects a 4-byte reply, which contains the response for the request/command.

MmmOST Check Request
SORREQDRVSUBREQXXXXXX
01h43hxy000000

MmmOST Print Spool Request (end spool)
SORREQDRVSUBREQXXXXXX
01h4Eh01h52h000000

Disk direct read/write requests

There are two functions, used to read/write individual 128-byte CP/M records on the disk. The blocks may be bigger than this, but the service processor handles blocking/deblocking the data to/from disk.  These are primarily used on Private and Public-only disks. On Public disks (with MmmOST managing shared files), applications can only read the directory blocks and are not allowed to write blocks directly.

Reading:
  • 10-byte IPC request to the server
  • 4-byte IPC response to the client
  • If no error was returned, 128-byte record to the client
Writing:
  • 10-byte IPC request to the server
  • 128-byte record to the server
  • 4-byte IPC response to the client
The request looks like this:
Read/Write CP/M disk record
SORREQDISKTK8RECLRECHTK16LTK16HWRTYPESEL
01h'R'/'W'0-11XRLRHTLTHWT0/1

  • The Disk is 0-11, corresponding to A through N
  • TK8 is an 8-bit value for the track number on the disk
  • RECH/RECL are two halves of the 16-bit value for the 128-byte record number of the track being accessed
  • TK16H/TK16L are two halves of the 16-bit value for the disk track (aka cylinder) being accessed
  • WRTYPE tells the service processor what kind of write to do - Async write for a data record (0), Sync write for a directory record (1), or (2) if it's the first write to the block, and the service processor doesn't need to preserve the data in the block's other records.
  • SEL is 0 or 1, depending on if the disk has been selected previously
Note that blocks are typically bigger than records, which are fixed at 128 bytes. Generally, blocks are a few KB in size - TeleVideo uses 2KB for floppies and 4KB for hard disks.

The response looks like this:
Read/Write CP/M disk record response
RETXERRNOPRNT
0-FFh0xy

  • Retcode is the CP/M 2.2 compatible return code, that would be returned in the A register for the CP/M function call. 0 indicates no error, and 1 indicates an irrecoverable error
  • X appears to be unused
  • ERRNO is the error code returned to the caller, usually one from the following table
  • PRNT is set to 1 to print the error returned
Read/Write CP/M error response codes
Error conditionValue
Error selecting drive0
Error reading drive1
Error writing drive2

BDOS function handling

The CP/M functions that work with files (using File Control Blocks, or FCBs) are intercepted for shared "Public" drives. Note that Public-only drives are different, and do not support shared file handling and locking, and Private drives can only be used by one user station at once, so those are both handled directly by the user station's CBIOS as a sort of remote block store.

The functions that are intercepted include:
Intercepted CP/M BDOS function calls
NumberFunction call
15Open File
16Close File
17Search for first match
19Delete file
20Read file, sequential
21Write file, sequential
22Make (create) new file
23Rename file
30Set file attributes
33Read file, Random
34Write file, Random
35Compute file size
36Set random record from seq. position
40Write random, zero fill block

All BDOS functions that are intercepted for handling by MmmOST on the Public drive send a request IPC and FCB, and receive an IPC response and modified FCB back from the service processor.  Depending on the function (and if there was an error), they will also send or receive a 128-byte record with the actual data in it as well.  This is only used for the 5 functions that read or write to a file.

The sequence looks like this:
  • 10 byte IPC request to the server
  • 36 byte FCB to the server
  • For Write/Write random/Write Zero fill, send 128-byte data record to the server
  • 4 byte IPC response to the client
  • 36 byte modified FCB to the client
  • If Read/Read random, and if there was no read error, send 128-byte data record to the client
The BDOS IPC request looks like this:
CP/M BDOS intercepted function request
SORREQLOGDRVFUNCUSERFNUMLFNUMHCBDCBFX
146h0-11xxflfhxxx



  • REQ is ASCII 'F' for a file request
  • LOGDRV is the currently logged on drive on CP/M
  • FUNC is the CP/M BDOS function call number
  • FNUMH/FNUML are 8-bit parts of the 16-bit file number used by MmmOST to keep track of the file
  • CBD is the "default" drive for file requests
  • CBF is "Current BDOS Function", which is probably the same as FUNC
The response looks like this:
CP/M BDOS intercepted function response
FNUMLFNUMHRETCODEERR
FLFH0-FFh0-FFh
  • FNUMH/FNUML are the MmmOST file number
  • RETCODE is the return code returned to the caller, typically 0xFF on error
  • ERR is the error code which CP/M can use to determine the fault
ERR is divided up into 3 fields. Bit 7 is set to 1 to print an extra second line of error, used for BIOS returned messages. Bits 6-4 are used for errors from the BIOS (problems reading/writing). Bits 3-0 are used for error from the data passed to the BDOS function. The list of error codes are:
MmmOST Error Codes
ValueError
0OK
1Command fault
2Write Protect
3Illegal Call
4Bad File for request
5Drive type bad for request
6Transfer out error
7Transfer in error
8Genrev does not match
9No space on drive
90hBad sector
98hRead only
C0hDrive selection error

Other programs

LOGON.COM is the most useful other program. It send a request to the server to switch your private directory to a different password-protected (or back to the default) directory.

The request follows the same 10-byte / 128-byte / 4-byte sequence from "Check" requests earlier.  One major difference is that the SOR byte is 00h instead of 01h.  The 128-byte data field contains the password as the first 8 bytes (and the remainder should be ignored).  The 4 byte response will have the first byte as 0 if the request was successful.

The Logon request looks like this, with DRV set to the CP/M drive number to attempt to log-on with the password:
MmmOST LOGON.COM Request
SORREQDRVSUBREQxxxxxx
00h43hx4Ch000000

Timing concerns

Testing this on TS-801s and TS-803s, they both use somewhat different time outs and have different timing requirements. I have observed that a 45uS sleep after requests are serviced before checking for CTS, a 1-5ms sleep before sending each boot loader block, and a 100uS sleep before sending data back to the client in the file handler was optimal to avoid times or sending data before the client was ready for it.

In other words, timing race conditions seem to be something that needs to be carefully handled in working with these systems.  The Z80 code mostly uses busy loops as delays or for timeouts, which are very clock speed sensitive.

Future work needed

There's more MmmOST software that needs to be reverse engineered, including print queue handling.

It'd be nice to also look at TurboDOS as another example of how this was done by a different CP/M compatible OS.

Also, I'd like to work on modifying SimH to simulate TeleVideo systems.  The RS-422 connection could be emulated with a network (or local) socket between two copies of the simulator.

A future blog will document my creation of a replacement for MmmOST, which runs on modern hardware.

References

Bitsavers's "bits" collection has the TeleVideo source code and some disk images. Dave Dunfield's collection of images has Imagedisk images for MmmOST on a TS-806, along with the software needed to read them. Online CP/M manuals such as this one have been very important in understanding how CP/M works. Datasheets for Zilog parts such as the Z80 SIO and other peripherals are valuable for understanding the code, along with the schematics in the TeleVideo service manuals on Bitsavers.


Wednesday, April 10, 2019

TeleVideo Systems Part V - MmmOST Introduction

TeleVideo Systems MmmOST Operating System

MmmOST, or "Multi-user, multi-tasking, multi-processor Operating System Technology" was a network operating system that TeleVideo developed and released along with their CP/M based computers starting in 1981.  The system was based around a central server running CP/M 2.2 and the MmmOST software, and connected to multiple client TeleVideo computer systems.

This differed from MP/M which was offered from Digital Research, because MP/M functioned as a multi-user operating system on one computer with multiple serial dumb terminals, allowing for time-sharing of a single CPU. MmmOST ran applications software on single-user computers, one for each person, and did resource sharing to allow multiple people access to the same data, while not constraining CPU and memory resources to a single computer.

TeleVideo TS-816 Service Processor

Hardware features

MmmOST ran on either a TS-806 or TS-816 (aka "System II" and "System III"), which supported up to 6 or 16 users.  These systems were called "Service Processors".  The 806 included 64KB of RAM, a 5.25" floppy drive for system maintenance and back-ups, a 10MB or 20MB ST-506 interface 5.25" full-height hard drive (with expansion to a 2nd external drive), and 6 ports to connect clients, or "User stations" to.  The TS-816 included 128KB of RAM, a tape drive replacing the 806's floppy drive, a 20 or 40MB ST-1000 interface 8" hard drive with an optional external expansion drive, and 8 or 16 User station ports.  Both systems included serial and parallel ports for shared printers and a console terminal. 

The system connected the server to the clients in a "star" network configuration, similar to how twisted-pair ethernet is connected to a network switch.  Each user station was connected back to the service processor over a 15-pin 100-ohm shielded twisted pair cable of up to 300 ft.  For shorter (up to 10 ft) runs, ribbon cables were usable as well.  The connection between the two used SDLC over an RS-422 link at 800K baud, which could transfer data at up to 80-100K Bytes/sec.  To make cabling simpler, the TX/RX pins were flipped between the service processor's and user stations' connectors, allowing for simple straight-through cables.

Example system layout from TS-816/40 manual

Software features

MmmOST provided a few basic features to clients - clients could boot off of the RS-422 connection back to the host (MmmOST required this to keep track of the state of each client and ensure that the client OS was properly configured for the system), and the server provided printer sharing/queueing and disk/file sharing to the clients.

Booting

Booting was integrated into the EPROM on the user stations, such that flipping one jumper (jumper 5 on all of the Z-80 based systems) would switch between booting off of a local disk or downloading the boot loader and OS from the RS-422 link. Every time that a program would exit on CP/M on the user station - or when the system was reset - it would re-download the OS from the service processor. The service processor would reset its internal state, releasing any resources that the user had exclusive access to.

Printer sharing

The system maintained print spools for each user station and controlled access to printers.  Users could request exclusive access to printers, or queue up print jobs without having to wait for access to the printer to continue.  This kept different users print jobs from mixing (such as when one person wanted to print payroll checks, and another print out a sales report, the sales report wouldn't get printed on the expensive check stock).

User stations all allowed either a serial or parallel printer to be attached locally as well so that local printers or modems could be used instead of the shared printer.

Disk and File sharing

MmmOST shared hard disk space on the server out to individual user stations.  Between this and the remote booting, the clients could be operated without any local storage at all.  Two systems, the TS-800A and TS-800R (based off of the TS-802 and TS-803), were full computers that shipped without any local storage options at all, and could only be run connected to a service processor.

On other systems where local storage was available (such as the TS-801, 802(H), 803(H), TPC-I, 1603, etc), the local floppy and/or hard drives were still available to the user, with names starting at N - typically M/N for the two floppy drives, and O/P for hard drives.  Note that CP/M 2.2 limited drive sizes to 8MB (2^16 x 128-byte records), so larger drives had to be partitioned into no bigger than 8MB chunks.

On the server, there were typically at least two hard drive partitions available to clients. One, which showed up as "A" on clients, was a "private" drive, which functioned similarly to a home directory or user profile.  Private drives were allocated one per user station, and additional password-protected drives could be "logged onto" using the MmmOST LOGON.COM program.  At the client, these appeared as a standard block-oriented disk to CP/M.  MmmOST would maintain separate directories and manage allocation space on the real disk to separate out users' files.  The data was stored in a mostly CP/M compatible way, so that doing a "dir" on the service processor drive that stored private directories without running MmmOST would show every users' files. 

One side note, these private drives were not the same as CP/M 2 and MP/M "users".  Changing the user number (from 0-15) separated out files on the same private drive. Under MmmOST, user numbers provided a way to separate out different files, since CP/M 2 doesn't support named directories of files.

The other main type of drive is a "public" drive.  This was shared between all of the clients.  The clients could directly access the blocks on the drive, but in general CP/M would handle file accesses by passing the call (with the appropriate FCB) up to the service processor, and get back the result with the new/updated FCB.  This is the type of file handling that MmmOST used for special files, such as MULTI.SYS, which allowed the user stations to make special requests to the service processor.  

Public drives also supported record-level locking of data, so that applications could be written which allowed multiple users to access the same database.  This would be useful for something like a client database where it would allow different people to work on the data in the database, without overwriting each others' updates and corrupting the database.

Public-Only drives are shared like Public drives, but do not support locking or access control to files.  They can be used for read-only access to programs or data, or for anything which will only be accessed by one user at a time, such as the floppy disk on the TS-806 service processor.

MULTI.SYS

Multi.sys was a "virtual" file that MmmOST kept on the shared drive, which allows for the user stations to make requests of MmmOST.  There were a few different sections of the file, which allowed access to the service processor time (to synchronize it with user stations), file locking requests, and inter-user communications.

Time access was the simplest, reading a certain record of the file returned the current time on the service processor.

Record locking allowed a program running on a user station to request access to a file.  It could request either exclusive whole-file read/write access, exclusive access to particular records, or just test for locks on the file.

Inter-user communication is a simple form of IPC, which allows messages to be passed between software running on different user stations.  The default mode is a "mailbox", which allows each user station access to a short 128-byte mailbox, which any user station can use to send a message to another user station. The mailbox is overwritten, with no history or queueing, every time a new message arrives for that user station.

As an alternative to the mailbox, named FIFO queues can be selected instead. There are default FIFOs for each user station and can be additional named FIFOs, which multiple user stations can share.

Other commands

There are other commands which are distributed with MmmOST that talk to the service processor directly, instead of using a file like MULTI.SYS.  These include LOGON (change which private drive is currently in use), DSTAT (to get information from the user processor about resources), PRNT/SELQUEUE (to control shared printing and queues), and a few others.

More info

My next blog post will go into more detail that I've reverse engineered while building a modern replacement for MmmOST.

The TeleVideo MmmOST 2.1 programmers manual has more details on the system, programming to use MmmOST features, and how to change MmmOST parameters. A copy is available on Google Drive here.

Tuesday, April 9, 2019

TeleVideo Systems Part IV - TS-801

TeleVideo TS-801 "System I"

The TeleVideo TS-801 was TeleVideo's first entry into the single-user CP/M computer market. It was also usable as a client system off of one of their distributed systems, which were centered around one of the TS-806 or TS-816 "Service Processor" systems.

The system is not expandable but arrives as a fairly well-configured system.  It was released in 1981 and listed for $3295 with a 4MHz Z-80, 64KB of ram, two floppy drives, a couple of serial ports, and a Centronics-compatible parallel printer interface.


Outside appearances

The TS-801 has a pair of double-sided/double-density Tandon TM100-2A floppy drives with up to 360KB of storage each, plus a red reset button on the front.  On the back, there's a DB-25 terminal port for the console terminal, a DB-25 serial printer port, and a 36-pin Centronics parallel port, along with a DA-15 RS-422 port to connect to a Service Processor for use in a distributed system.





Due to how the machine is set up, you can either use the RS-422 port or the serial printer port.  To switch between the two, there are a set of jumpers on the motherboard that need to be cut and re-soldered differently.  This is probably the worst way I could think of implementing this.  However, I'm sure that it was expected that you would only change this at most once when the system was initially installed.


Disassembly

The inside of the TS-801 is fairly simple and designed to be serviceable. The major chips are socketed, and the floppy drives are mounted to a platform that hinges out of the way for access to the system board.  The power supply is a fairly simple open-frame unit, which provides +5, +12, and -12V to the system board and floppy drives.





I have noticed that there are some differences between the two TS-801 systems that I have. The second system has several chips soldered into the "spare" locations on the board, and a bunch of fly-wire connecting them into various parts of the board.

Also, there are the set of jumpers on the motherboard which need to be adjusted to switch between using the 2nd (printer) serial port, or the RS-422 port.


System Design

The system overall is fairly similar to the TS-802 that came out a few months later.  There seem to be two main differences: The system requires an external terminal, versus the integrated terminal in the TS-802.  The system also has one less SIO chip, which means that there are fewer serial ports available.

The main chips in the system are:
  • 4MHz Zilog Z80A CPU
  • Z8410 DMA
  • Z8442 SIO/2 - RS-232 terminal and either RS-232 printer or RS-422 link
  • Z8420 PIO - Centronics serial interface
  • Z8430 CTC - Time of day interrupt, baud rate generation
  • WD1793 - FM/MFM floppy drive controller
  • 16 x 4164 64k x 1 150ns DRAM 
  • 2732 - 250ns, 4K x 8 EPROM
The same code will (mostly) run on either system, because they both use the same controllers, at the same IO port addresses.  The TS-802 moved the floppy (and an optional hard drive controller for the TS-802H) off to a daughter card, which made things a bit more modular.

Trying to get the system working, I ran into several issues.  I'm still not convinced anyone had these systems actually working before I got them - the hardware seems flaky, but the hardware mods to the second system seem to make them slightly more stable.  It would randomly reset occasionally while running CP/M programs.

Knowing they were similar to the TS-802, I grabbed the 802 service manual off of Bitsavers and started comparing it to the system board.  Most of the schematic matched up, but some parts around the memory latch -- the circuit that selects between having the boot EPROM or DRAM selected in memory -- was different.  

I hooked up a logic analyzer, and sure enough, the latch seemed to randomly be flipping between EPROM and DRAM.  Sitting at the CP/M command prompt, this wasn't really noticeable, because it only affects the bottom 16K or so of RAM, and CP/M and the SSP command interpreter lives up in the top 16K of RAM.

Comparing the wiring to the schematic, It appeared that the system controlled the latch based off decoding the address bus, and the buffered /IORQ line. On the 802, the system uses a signal it calls /IOWR, generated from the buffered /IORQ and /WR lines.

At first glance, just using /IORQ seems like it should work, but will set the latch anytime you do an IN/OUT instruction to address 04 (really, 4 or 5, because the bottom address line isn't decoded), and re-set the latch to enable the EPROM at address 6.

However, in processing an interrupt on the CPU, /IORQ and /M1 are used to signal an interrupt acknowledge and get the interrupt vector from the interrupting device. If this occurs when the processor is executing code with the bottom 8 bits of the address in the range 04-07, it'll trip the latch to potentially change state.  The system has a periodic timer generating interrupts to track the time of day, which will occasionally cause this to happen.  If that happens when you're executing code in the bottom 16K of RAM, it will instead execute code from the PROM - either crashing the system or (more commonly) resetting it.

This problem manifested itself as randomly rebooting the system when I tried to execute CP/M programs.  If the latch was set to use the EPROM, when CP/M tried to jump to the program at address 100h, it would instead jump to the EPROM at address 100h.

I was able to fix this by severing a trace for /IORQ, and running a jumper wire to connect /IOWR as a control input to the latch instead.

After this change, I was able to get this second system to mostly work, using the RS-422 port to boot off of a service processor.

Thoughts

These systems were pretty rough, though they meant to be cheap business systems.  They came as un-expandable systems but were fairly maxed-out for a single-user system, with a full 64KB of RAM, and two floppy drives.  TeleVideo quickly released the TS-802 for about $200 more, which included an integrated terminal, and made the TS-801 obsolete.  The TS-801 seems fairly obscure, and I doubt many people bought them with the lower priced TS-802 (and later, TS-803 and TPC-I systems) coming out over the next couple of years.

Today, they're sort-of nice because they're easy to connect to a service processor and serial terminal server to development work on without needing physical access to the system.  The biggest problem is that they still seem to have design issues which make the big red reset button on the front is important to have access to.  The Tandon floppy drives seem to be fairly unreliable; I'm not sure if that's due to age, or just their generally low quality.  The Teac FD55 drives in later machines are less expensive, smaller, and in my experience are way more reliable.