Software

Table of Content











Content


LC-81 Software is currently in its infancy. It is being written in assembly language using an Linux PC as developer machine (it was done in a MS-DOS PC before).

Applications can vary from stand-alone programs taking control of the whole machine, to better structured ones relying on system software existing in the MC-EPROM or previously loaded into RAM.

A primitive operating system (LCCP) is currently under development.

System Software

MC-EPROM

The MC-EPROM occupies 8 MB from address 0000H to 1fffH. It contains support for the START button in the Console (NMI interrupt), RST instructions, and some utilitiles, notably an Absolute Loader that allows to bring software into RAM from external media via RS-232.

Organization of the MC EPROM

When the LC-81 is powered-up or reset, a bus request is asserted to the microprocessor so execution at address 0000H can not really start. Nevertheless, an idle loop (jump to 0000H) has been placed there to prevent further execution in case the BUSREQ line is not fired on time.

It follows the table for RST instructions starting at address 0008H. This table is filled with jumps to corresponding addresses in RAM from 2048H to 207fH so external software can make use of RST instructions without having to burn the EPROM. This brings support for RST 08H to RST 32H.

At address 0066H is a jump to a START routine in support to the Console's START button. Then a series of utilities follow.

This content, currently at version 0.44, is temporary. In the future I may find a better use for those 8KB or memory.

Support for the Console START button

When the Operator presses the START button in the Console, an NMI interrupt request is asserted to the microprocessor which service routine is at address 0066H. This address contains a jump to the START routine listed below:

START
    in    a, (SARL)
    ld    l, a
    in    a, (SARH)
    ld    h, a
    jp    (hl)

SAR is the Start Address Register (16 bits, split in two 8-bits registers SARL, SARH) located in the Console Register Board in the MC Unit. This register is loaded from the Console's swiches when the LOAD button is pressed, then loaded into the Program Counter register (PC) by this routine (as we can infer from the code) when the START button is pressed.

These seven bytes of machine code is the only software support that the Console requires. The rest of its functionality is based on hard-wired logic.

Absolute Loader

Hex to Bin conversion routine

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

Tool-chain and Utilities


MS-DOS

LC-81 development was done in a MS-DOS PC for a long time. I utilized or developed the utilities and simulation programs listed below.

Third Party Tools

TASM Macro Assembler

Before moving to Linux, all assembly code in this project was assembled using the TASM Macro Assembler version 2.9 (1992) by Speach Technologies Inc. --Available for download at:

  • http://www.z80.info
  • dBFast Text Editor

    This is a good text editor for MS-DOS produced by SemWare in the late 1980's.

    Borlan Turbo Pascal 3

    Some utilities and simulation programas such as COPYCOM and VTCOM, running in my developer machine (MS-DOS), have been produced using Borland Turbo Pascal 3. This historical version of Turbo Pascal has been released by Borland for free use and is availalbe for download in the Internet.

    Home Made Tools

    MKHEX

    COPYCOM

    VTCOM

    Applications

    Applications are programs running at the LC-81 minicomputer for doing tangible work. Most of these are written in Z80 assembly language but it could also be cross-compiled in MS-DOS PC using some superlanguage cross-compiler.

    General guidelines

    Applications are assembled separately from the OS, with origin in address 3000H.

    You must include the header file LCCP.H which contains declaration needed to access LCCP resources. If working with virtual or real tape devices, you must also include VT.H.

    LCCP services are accessed using the instruction RST 08H, as following:

        ld  a, service-number
        ld  b, function-number
        rst 08H

    Correct service-numbers and function-numbers are declared in LCCP.H.

    Required parameters (service/function dependent) must be passed in the Stack according to a well defined "arguments frame".

    To access peripherals, use the DEVMAN service. You will need to build a request struct (REQ) in application's area and pass a pointer in the arguments frame.

    For error handling, use the following macros:

        SETERR (error-code) ; Report error and continue
        PANIC  (error-code) ; Report error and halt
        GETERR              ; Return current error in register A
        CLRERR              ; Clear error

    error-codes are 8-bit numbers. Some are defined in LCCP.H. You can add your own application-specific error codes if necessary.

    Never include the file UTILS.ASM, intended for LCCP internal use, because references to addresses  in that code are subject to change as LCCP grows. Similarly, never read or write the ERRORCODE variable directly; by doing that you would bypass LCCP functionality regarding error handling.