Search   
Home  Print View  

 

Software

Branch Content

LCCP Operating System

LCCP is a primitive operating system being developed for the LC-81 minicomputer. Its main purpose is easy the production of application software by providing abstraction from the hardware.

The OS is monolitic, single-programming and single-stack. It is oriented to batch processing and brings support for tapes used for mass storage.

Being a batch processing system, LCCP offers no interactive user interface of any kind. Instead, a Job Control Language (JCL) is employed to define the execution work flow.

Abstraction from hardware is provided through device drivers, which code is part of the operating system and therefor is assembled with it. All hardware interrupts are captured by LCCP and redirected to the apropriate device driver's ISR routine.

Extended functionality can be implemented by writting and installing run-time libraries for which the system provides the appropriate support.

LCCP code, in general, is a collection of well structured routines accessible to application via software interrupt RTS 08h. The only LCCP routine that needs to run prior applications is a service named INIT which performs system initialization tasks such as setting the interruption mode, positioning the stack pointer and initializing peripherals via device driver's INIT routines. After running to completion, INIT falls into and idle loops waiting for JCL commands. These commands come from a "batch tape" mounted on the "default tape drive".

LCCP itself is loaded into memory from tape as an image using the "Absolute Loader", a program reciding in the MC EPROM. Once loaded, the Operator must start LCCP by entering its starting address in the Console, then pressing the Start button. This will run the INIT service to completion to finally fall into an IDLE LOOP.

At this point, the Operator must mount a batch tape containing a JCL script (possibly followed by a program) in the default tape drive.

Application programs and libraries are loaded and relocated in a Heap (dynamic memory) and can in turn make use of the Heap for their own purposes.

LCCP stands for LC-81 Control Program, a term borrowed from IBM System/360 terminology.

Main Features:


  • Single Programming, Single Stack.

  • Peripherals managed by Device Drivers.

  • Hardware interrupts handled by the OS and redirected to device drivers.

  • Dynamic Memory Managment.

  • Services accessible via software interrupts (RST 08H).

  • Support for installable run-time libraries.

  • Batch Processing

  • Job Control Language (JCL)



Architecture

The LCCP code is monolitic. It is loaded into memory as an image containing not only code but also the data structures pre-initialized. This includes information about peripherals (DEVTAB), interrupts (INTTAB) and device drivers. When a new peripheral is hooked to the LC-81 hardware, a new device driver must be written for it, the DEVTAB must be updated in source and LCCP as a whole must be re-assembled and reload into the LC-81.

LCCP consists of the following components:


  • Service Manager (SERMAN)

  • Services

  • Utilities

  • Start routine

  • Interrupt Indentification Routines

  • Device Drivers


Most of the LCCP code recides in Services and Device drivers. In the future, installable run-time libraries (RTL) will also play an important role.


Passing Parameters

Parameters between application programs and LCCP are passed in three ways:

  * Registers A and B (only)
  * Stack
  * Request struct (REQ)

Registers A and B are used when requesting services via RST 08H (A=service number, B=fuction number) as we shall see later in this section. This is the only place where registers are used for passing parameters to LCCP, and no register other than A and B are used for this purpose.

The Stack is used for passing other parameters to the service being called, if required, and to return parameters from the service back to the program. The arguments frame in the Stack is service/function dependent. The Program is responsible for clearing the arguments frame from the stack once the service has returned.

The Request Struct (REQ) is used to pass parameters to device drivers. The struct per se is allocated by the program in its memory area, then a pointer is passed in the device's entry in DEVTAB. We will cover this in details later in this section.

LCCP never uses shadow registers so application programs can safely make use of them for their own purposes.

Managing Hardware Interrupts

Data Structures

The LCCP image occupies the first 4KB of LC-81 private memory (2000H-2fffH). LCCP variables are placed in the first portion of this space starting at 2000H with the interrupts table (INTTAB).

The stack pointer (SP) is initialized at the bottom of private memory (5fffH) and expected to grow up util a limit labeled HEAD_ROOM. Only one stack (pointed by SP) is used for both LCCP and applications code.

The space between HEAD_ROOM and the bottom of application code can be used for dynamic memory (HEAP) by applications, for which LCCP offers support through the MEMMAN service. The HEAP can expands to shared memory if present.

LCCP itself does not utilize dynamic memory; all its structures consists of fixed-size arrays.

Interrupts Table (INTTAB)

Restart Table (RSTTAB)

Services Table (SERTAB)

Devices Table (DEVTAB)

Global Variables

LCCP Components

Devices Drivers

Device drivers take care of device-specific functionality isolating the rest of Software from these details by presenting a common interface to them. This interface is the Device Manager service (DEVMAN).

Architecture

A device driver consists of three separate routines:

  * Initialization Routine (INIT)
  * Device Service Routine (DSR)
  * Interrupt Service Routine (ISR)

The INIT routine initializes device hardware (for example, programmable chips) and data structures associated with the device driver.

The DSR routine is called to initiate operations on the controlled device. In most cases, the DSR sends commands to the device, returning immediately after that. The device's response could take some time and it is captured through interrupts.

The ISR routine services the interrupts coming from the device in question. Possibly the first thing to do is to query some device's control register to learn what caused the interrupt.

The ISR also performs part of the useful job. For instance, when reading from a Tape Drive, it receives the incoming bytes into a given buffer.

In source, device drivers are written in separate files with extension ".DEV".

Existing Device Drivers

VTCOM.DEV

VTCOM.DEV is the device driver for the VTCOM Tape Drive Simulation Software running at MS-DOS PC connected via Serial Port (RS-232) to LC-81. This program simulates a Tape Drive according to the "LC-81 Tape Drives Specification". (See the specification in section "Specs").

The LC-81 serial port is based on UART chip 16C550. The INIT routine initializes the chip to use FIFO and to interrupt for each byte received.

Since there is no EXT-BUS in this case, Drive's registers C and F can not be accessed directly. Access to those registers is orchestrated by the mean of 3-bytes "Escape Sequences". This method, however, presents a limitation for interrupts associated to the WRITE operation: it is not practical to write one byte at the time (as specified for Tape Drives) so the ISR routine writes a whole block continuously in a loop before returning.

We will cover VTCOM Operations and Escape Sequences in details later in section "VTCOM Tape Drive Simulation Software".

Service Manager (SERMAN)

Services

ERR

ERR_CLR

ERR_GET

ERR_SET

ERR_PANIC

INIT

DEVMAN

The Device Manager Service (DEVMAN) is the only way for applications to get access to peripherals, or, put in another way, it is the common interface between applications and device drivers. DEVMAN relies on the Device Table (DEVTAB).

Access to peripherals through DEVMAN also relies on a "request structure" (REQ) that applications need to construct as part of the requesting procedure.

The DEVMAN service has five functions:

  _INIT     Initialize device driver
  _PUTREQ   Attach REQ struct to DEVTAB
  _CLRREQ   Detach REQ struct from DEVTAB
  _EXEC     Execute device operation
  _SETDEV   Populate DEVTAB entry

The _INIT function calls the INIT device driver routine.

The _EXEC function calls the DSR device driver routine. Specifications for the operation being requested is in the REQ struct. The application must built the REQ struct and attach that to the device's entry in DEVTAB prior to call _EXEC.

The need for re-entrancy

A given device driver can be shared among alike devices. Moreover this is a common case. And shared device drivers arises the need for re-entrancy as we will show in this section.

Let put the case of a program transfering a file between drive A and drive B, both using the same device drivers.

Drive A serves data one byte at the time asserting an interrupt when a byte is ready for reading. The interrupt service routine (which is part of the device driver) must accomodate incomming bytes into a given buffer.

Similarly, drive B accept data one byte at the time asserting an interrupt when it is ready to receive one. The interrupt service routine must pull a byte for the buffer and update pointers as necessary.

We could make the buffer the size of a block, read a block entirely from A, then copy it to B, but this would be inneficient: the time between interrupts in this case is so long that the microprocessor could easily execute 400 intructions in that period. A much eficient solution is to interleave the two operations (read and writting) taking advantage of the waits between interrupts.

This implies that the device driver will be alternating roles. Now is reading, then is writing; now is working with device A, then is working with device B, and so on. Synchromizing these activities could prove complex, specially if we want to extend this behavior to an arbirtrary number of operations: say for instance that we also want to copy the file to a third drive C.

A better solution is to isolate the "context" in which the device driver must operate in each case. That "context" is provided by the REQ struct. We create a struct for each device being operated and attach a pointer to it in the DEVTAB entry for that device. REQ contains, not only the request parameters but also the variables that the device drivers will utilize internaly.

This way we re-enter the device driver code with a different "context" each time, so it never gets confussed. Yet the code is simpler because it doesn't have to take care of synchronization.


The Request Struct (REQ)

DEVMAN_INIT

DEVMAN_PUTREQ

DEVMAN_CLRREQ

DEVMAN_EXEC

DEVMAN_SETDEV

MEMMAN

LIBMAN

Utilities

HEXTOBIN

GETENTRY

Start

LC-81 Homebrew Minicomputer -- this software is based on Help Books running at melissa