       UZI180 - Unix Z80 Implementation for the Z-180 (UZI180)
    Adapted from UZI by Doug Braun and UZI280 by Stefan Nitschke
               Copyright (C) 1998 by Harold F. Bower
                    <HalBower@worldnet.att.net>
          Portions Copyright (C) 1995 by Stefan Nitschke

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License (file LICENSE.TXT) for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

UZI180 is a multi-tasking, multi-user operating for the Zilog Z180 family
of microprocessors.  It is a port of the original UZI incorporating many
fixes from UZI280 and some additional features.  Significant changes
include an updated floppy disk driver based on a NEC765 derivative cont-
roller, a restructuring and reorganization of the various kernel modules
to ease porting to different hardware platforms, a simpler loading
structure from CP/M, and an integral (albeit minimal) CP/M 2.2 emulator.
The task was undertaken to learn more about the Unix operating system and
the C programming language with an eye to using it to replace several
aging CP/M systems.

Like the original UZI, this system is based on Unix Edition 7 which was
one of the direct predecessors of Unix System V.  UZI180 incorporates the
original system calls and the UZI280 extensions added by Stefan Nitschke.
Some minor additions were also added to provide additional System V compa-
tible features.  As stated in the original documentation, there are only a
few significant differences in the UZI kernel calls.  The principal
deviations are:

 - 32-bit return values are not provided, so data structures are passed
   via pointers to pass essential information.  "seek" is therefore incor-
   porated as a kernel function with "lseek" provided as a library call.

 - The system "times" are reported in a different format than the standard
   32-bit count of seconds since 1 January 1970.  32-bit Date/Time struc-
   tures are in the packed binary form used in IBM PCs, therefore
   resulting in 2-second granularity.  As a convention, the 0-99 year
   should be interpreted as: 70-99 implying 1970-1999, and 0-69 being
   2000-2069 to provide Year 2000 compliance.  Standard functions are
   available as library routines.

 - The "stat" call has a different file length format due to the 32-bit
   length omission and use of only 16-bit block and inode numbers.  A data
   structure is used to contain the information (see library source for
   details).

ARCHITECTURE:

UZI180 makes extensive use of the Memory Management (MMU) of the Z180 core
to provide as much process isolation as possible with the chip.  Since the
MMU granularity is 4k, this establishes the basic process structure.  The
top 4k of logical address space (one MMU slice) is defined as Common1 memory
space and contains critical routines that either must always be resident in
memory, or are associated with the currently-running process. Some of the
key elements placed in the Common memory space are:

	- Interrupt Handlers
	- Process Swapper
	- System and Interrupt Stack Space
	- User Process Data Structure
	- Kernel Service Handler

A full 64k is assigned to each process, with 60k available to application
code and data, with the remaining 4k used for system storage.  This
organization permits one 64k bank for the kernel/common memory and seven
process banks on a 512k system where all RAM is usable.  The UZI180 memory
map therefore appears as:

	          First 64k	      Subsequent 64k banks
	FFFF	+------------+		+------------+
    Common	|   Common   |		| Task Store |+
	F000	+------------+		+------------+|+
		|            |		|            |+|+
		|   Kernel   |		|  Process   ||+|
    Banked	|    Code    |		|    Code    |||+
		|            |		|   & Data   ||||
		|            |		|            ||||
	0100	+------------+		+------------+|||
		|  Reserved  |		|  Reserved  |+||
	0000	+------------+		+------------+|+|
					 +------------+|+
					  +------------+|
					   +------------+

Areas marked as "Reserved" are used for Restart and Interrupt vectors,
and to host the required Page 0 storage elements for the CP/M Emulator.
Constructing executable code modules starting at 100H also allows use of
normal CP/M tools without conflicting memory use.

The architecture accomodates MMU base addresses beginning at the first
available RAM address wherever that is.  For YASBEC and P112 systems that
shadow the ROM into RAM, the initial BBR (MMU Base Bank Register) reading
is 08H, while on the MicroMint SB180 RAM begins at 40H.  64k Banks will
therefore begin with BBR values of 08H, 18H, ..., 78H for the former; and
40H, 50H, ..., 70H for a 256k SB180.

For 256k systems that have all RAM available (i.e. do NOT shadow the ROM),
the kernel and only three process banks will be available.  While this may
be minimally usable for a single-user system, a 512k system is the minimum
recommended.  For systems which DO shadow the system ROM, 32k is normally
reserved (beginning at physical RAM address 0), thereby reducing available
RAM by 32k.  This leaves 480k available in a 512k system resulting in only
six process areas after allowing 64k for the kernel and common bank.  The
last 32k will be unused.

The algorithm for computing true 20-bit addresses in a Z180 (for DMA) is:

	BBR	7654 3210
	Addr	     hhhhhhhh llllllll
		----------------------
		bbbb xxxxxxxx yyyyyyyy

An initial check should be made for common area access (assumed to be the
top 4k) which will be addresses of the form "1111hhhh llllllll" or FxxxH.
For these addresses, the raw BBR value for the first 64k of usable RAM is
added.  As an example, the first byte of the Common Area is computed for
DMA use on a P112 with 512k of memory (ROM shadowed) as:

	BBR	0000 1000			08
	Addr	     11110000 00000000		 F000
		----------------------		-----
		0001 01110000 00000000		17000

A kernel variable (osBank) for the Common Area holds the BBR value for the
top 4k which is always accessible by the processor.  It is used to specify
the source address when swapping out, and for the destination address when
swapping in.  The BBR value associated with each process space is stored
both in the kernel process table, and in each process' user data block.
This value is used for the other address in process swaps.

Task switching consists of saving the current process status in the User
stack and data area, block moving the user data area and stacks to the
respective process area in the reserved top 4k (via DMA), restoring the
new task's data into common memory (also via DMA), changing the Bank Base
Register in the Z180 MMU to bring the new task in context, resetting the
new process' stack pointer, restoring processor status and continuing as
before.  This results in relatively rapid response since the bulk of time
required is due to two 768-byte DMA transfers which consume 1536 * 6 T-
states or 9216 / 18,432,000 = .0005 Sec = 500 microseconds on a 18.432 MHz
P112 or YASBEC upgraded with the Z8S180, and double this time, or 1000
microseconds (1 millisecond) on a standard 9.216 MHz YASBEC.  This ignores
any additional time due to insertion of memory wait states and the
relatively insignificant overhead associated with housekeeping.

Time slices were initially set to 50 mS (20 ticks per second) based on one
of the internal Z180 Programmable Reload Timers.  At that rate, less than
4% of a time slice was consumed in swapping processes.  The development
systems have subsequently shortened the slice time to 20 mS to minimize
data loss from polled serial ports.  A disadvantage of shortening the time
is that a greater percentage of CPU time is consumed during process swaps
with a subsequent lengthening of task run times.  This may be adjusted by
appropriately setting parameters in the C and Assembly include files and
re-building the kernel.

Since peripherals in small systems often are not capable of Interrupt-
driven operation, UZI180 implements polling of such ports during timer
interrupt cycles.  Unfortunately, this can lead to inaccurate timekeeping
if the timer is the sole source of Real Time in the system.  A prime
example of this is in the Floppy Disk Driver.  Polled operation often
dictates that interrupts be disabled during disk accesses to avoid data
loss and/or corruption.  During the comparively long time which may elapse
(up to one revolution of 200 milliseconds for 3.5" drives), several clock
ticks may be lost before timekeeping resumes.

Wall-Time is corrected automatically by reading the Real-Time Clock every
time the number of ticks-per-second (set by a constant in include files)
elapses and updating a global kernel variable.  This variable is then used
by kernel functions when necessary to perform such tasks as time stamping
files and returning current time-of-day.

Polled IO sampling on timer interrupts is not so easily handled and data
loss may be significant.  For systems which must operate in this manner,
it is recommended that low-priority processes be placed on those inter-
faces.  Also, use of peripherals that disable interrupts for long periods,
such as polled Floppy Disk drivers, should be minimized.


OPERATION:

The UZI180 kernel is Process 0 and executes in the lowest 64k of RAM in
the system as a standalone CP/M executable program.  Several stages of
initialization occur when first started.  The code and initial data which
is destined for the Common Memory is moved into position, having been
linked to an executable image during the link process.  After initializing
various kernel data elements, Process 1 is initialized in the lower 60k of
the next higher 64k memory region.  Normally Process 1 is "init" which
logs in users and starts (forks) a shell or other user interface.  Each
consecutive fork without termination of a previous process is loaded into
the lower 60k of successively higher 64k increments.

When a new process is initiated, data in the process' address is prepared
with several pieces of data.  Necessary arguments and environment
variables are copied into high addresses along with the pointer arrays per
standard UZI definitions.  Location 0H is initialized with a jump to a
TRAP (illegal instruction fetch) error handler, and location 0030H (RST
30H) with a jump to the kernel service handler.  Both of these handlers
reside in the Common Memory bank beginning at 0F000H.  Version-specific
locations may also be initialized at this phase such as the Interrupt
Vector at 0038H (RST 38) and the Non-Maskable Interrupt at 0066H.

Process swaps are initiated by a simple timeout of a per-process counter
which is decremented.  When a process is initiated (via an execve kernel
call) or the process is swapped into context, the counter is initiated to
a defined priority value.  Each "tick" of the periodic clock interrupt
decrements the counter until it reaches zero.  At that time, the User data
and associated stacks (User, Swap and Kernel) are copied to the highest 4k
of the respective process' memory space via DMA, data for the next
runnable process is loaded into the Common area, and the new process is
swapped into context by remapping the Bank Base value of the Z180's Memory
Management Unit.

UZI180 kernel service calls are initiated by pushing necessary parameters
and the desired function number on the stack and executing a Restart 30H.
A jump to the service handler in common memory was placed at 0030H during
process initialization.  Since a Restart functions as a one-byte CALL, a
return address is placed on the stack below the function number.  The
Service handler obtains the stack address, switches to a kernel stack in
common memory, extracts information (function number and parameters) from
the user stack, switches the kernel in context, and passes control to the
kernel function process handler.  When the function completes (except for
_exit), status information is passed to the kernel exit code in common
memory, the user process is restored to context along with the user stack,
and the Restart "returns" to the user code.  Parameters are pushed onto
the stack in standard C form of right to left as specified in the function
declaration (contrary to UZI and UZI280).  This places the left-most
declared parameter immediately above the return address on the user stack.

Executable programs in UZI display the CP/M roots of the system in that
all programs begin execution at 100H.  In melding the original Unix tech-
nique of testing the first character(s) of a file for "magic numbers" with
CP/M, all executable programs in UZI require that the first instruction be
a long jump (JP xxxx, opcode 0C3H).  For native UZI applications, this
header is contained in the crt.obj file linked with all executable files.
UZI180 modifies this file to follow the jump with a text string containing
the three letters 'UZI'.  If these three letters are missing but the
"magic number" 0C3H is present, then a CP/M executable is assumed and
execution commences under a CP/M emulator described below.


EMULATOR:

In addition to the UZI kernel service calls used by native applications, a
minimal CP/M 2.2 emulator is built into UZI180 to allow many existing CP/M
and Z-System utilities to run under UZI180 without modification.  The
emulator intercepts CP/M Bios and Bdos function requests and interprets
them into equivalent UZI kernel calls, or processes them directly.  To
minimize the amount of memory consumed, only essential functions are
included, and not all programs will function correctly.  Some of the more
significant limitations of the emulator are:

	- No Z-System data structures (ENV, IOP, RCP, FCP) are included
	- Only Drive A:, User 0 (as the current directory) is available
	- No ALV bits are included so Directory programs will be unable
	      to report free space
	- The Bdos size is smaller than 3.5k so programs assuming standard
	      components by subtracting from Bios WB address will be wrong
	- No Command Processor is included

When needed, the emulator is moved into high process memory below the env
and arg parameters.  Instead of transferring execution to the program at
100H as an UZI executable, the process begins by executing the base code
of the emulator.  Code at this location is later overwritten by the CP/M
stack since it is only used during process initialization.  This sequence
sets up CP/M vectors at 0H (Bios Warm Boot), 5H (Bdos service vector),
catenates argv parameters into a capitalized command line tail in the
default buffer starting at 0080H, and initializes other storage locations
(Current Drive/User, FCBs, etc) before beginning execution at 100H.  CP/M
program termination by executing either a Bios Warm Boot or Bdos function
0 will trigger an UZI _exit kernel function and terminate the process.

In the initial form, env parameters are not used by the emulator, but use
of the PATH variable is planned to make the system more usable.


DEVELOPMENT ENVIRONMENT.

The bulk of UZI is programmed in the C language under the CP/M 2.2 or
compatible operating system.  Doug Braun's original UZI was written with
the Codeworks' QC compiler which resulted in some odd interfaces since
parameters were passed the system stack in backwards order (left-to-right)
instead of the conventional right-to-left ordering used in the C program-
ming language.  UZI280 and UZI180 were both written for the Hi-Tech ANSI
Standard compiler Version 3.09 released for non-commercial use in 1987 and
included in Walnut Creek's CP/M CD-ROM.  The Hi-Tech package also included
a Z80/Z180 assembler, linker, librarian and source code for the CP/M run-
time library.  This system forms the complete development environment
(less text editor) for UZI180.

The Hi-Tech Compiler requires a rather large Transient Program Area to
compile the UZI sources.  Consequently, users normally operating with the
Z-System may need to remove several of the standard ZCPR3.x components to
free up enough memory to compile the system.  UZI180 was developed on the
author's Banked and Portable (B/P) Bios system with banked ZSDOS2 and
generated using autosizing of the system (BDOS and ZCPR) with no RCP or
IOP.  Since the compiler overwrites the Command Processor, the base of
BDOS forms the upper limit of Transient Program Area which is 0E486H in
the author's system.  The BIOS Warm Boot vector is at 0EE03H.  This is
only slightly larger than a standard 62k CP/M system where the BDOS vector
is at 0E405H and BIOS WB vector at 0F203H.  A standard 62k CP/M system
should be sufficient since the majority of initial work occured on a
slightly smaller system.

Following Stefan Nitschke's lead with UZI280, assembly-language modules
were added to several portions of kernel code to provide platform-
dependant interfaces where necessary.  Assembly code modules were also
added to improve performance, particularly to minimize context switches
and change parameter passing to the standard 'C' conventions of placing
parameters on the stack from right-to-left.  In some cases, these assembly
modules were necessitated by apparent problems with the Hi-Tech compiler's
ability to handle inline assembly with optimization turned on.  To avoid
severe performance penalties and code bloat, all modules were compiled
with optimization turned ON.

When bringing up UZI180 for the first time, you must first extract all
member files in the basic Kernel library module, and member files for the
specific version for your platform.  Begin by tailoring values in the
version-specific files to reflect your needs, particularly the Hard Disk
parameters in HDCONF.H.  Check the Z180 initialization values in both
Z180ASM.ASZ and ASMDEF.I to insure that such items as Serial Data rates
and CPU clock frequency are accurate.

You must next Compile/Assemble all source code C and Assembly modules for
the kernel.  The simplest way to perform this is to execute the SUBmit
script CMPILUZI.SUB.  In selecting the software tools for this task, use
either the original CP/M 2.2 SUB.COM or an equivalent such as SUPERSUB.
XSUB, EX and ZEX cannot be used since they install in high memory as a
Resident System Extension (RSX) below the Command Processor, thereby
reducing Transient Program Area space and creating problems for the
compiler.  Development of UZI180 used SUPERSUB under the B/P Bios/ZSDOS2
system described earlier.  NOTE: with Operating Systems such as ZSDOS
which do not re-log hard disk partitions on warm boots, the $$$.SUB
temporary file is not re-detected to initiate the next line.  Relogging
of the hard disk can be forced in a ZSDOS system by executing the ZSDOS
utility RELOG.COM after each line of the submit script.  All but the
last line of the script should reflect the appended command when placed
in the Multiple Command Line buffer.  An example compile command line
from CMPILUZI.SUB is:

	c -o -c -v process.c;relog

After each module is compiled/assembled, either manually or via the SUBmit
script, the components must be linked into a loadable kernel image.  A
script is provided for this purpose which is read into the Hi-Tech linker.
The script is set to produce an output file of UZI.COM which appears to
CP/M as a normal COMmand file, but siezes complete control and becomes the
UZI kernel when executed.  This method of using the script with the Hi-
Tech linker is needed to handle the case-sensitivity of segment names in
the Hi-Tech relocatable format.  Invoke the linker redirecting input from
the script file as:

	link <linkuzi

To aid in system maintenance and debugging, a symbol table (UZI.SYM) as
well as the UZI.COM kernel image is also generated in this step.

TOOLKIT.

Doug Braun provided several utilities which allow you to make an UZI file-
system and prepare it for use.  These must be compiled and linked with
modified versions of UZI kernel modules to form CP/M executable programs.
Some of the kernel modules are simply compiled with the utility include
files, while others such as the process and call modules are actually
older versions of the kernel code with some modifications to run as CP/M
programs instead of assuming total control of the computer.  In the case
of the core Floppy Disk driver, XFLOPASM.ASZ, hardware timeout via the
Interrupt timer is removed for utilities in favor of countdown timers and
letting the Floppy Disk motors remain running after the initial spinup.

The basic tools are:

MKFS - Make an UZI file system on a block device (HDx, FDx) and prepare it
        for use.

UDP  - Display specified UZI File System Block Number on the Screen in Hex.

FSCK - Check an UZI file system on a block device for consistency.

UCP  - Copy files between CP/M and an UZI storage device, type files to
        the CP/M Console, mount/umount another device into the UZI file-
        system and delete files.  If you are running ZSDOS with file
        date/time stamping, this program also adds date/time stamps to
        the UZI file system when copying files to the UZI file system.

Portions of the UCP utility were extracted into stand-alone UZI applica-
tions during UZI180 development to test various functions of the kernel,
and to form the necessary core suite of utilities to make the system
"feel" like a Unix system.  These are also provided along with a re-
written LIBC library containing the UZI180 interfaces.  A separate .DOC
file details the steps needed to compile and link applications to form
native UZI180 executables under CP/M.  UCP may then be used to move the
resulting executables into an UZI file system from where they may be
executed.


THINGS TO DO. (Beta Notes, 18 August 1998 - HFB)

- Add complete hardware initialization (probably in MACHASM.ASZ) to set
  all system parameters.  Currently, UZI180 relies on settings performed
  by CP/M or compatible OS/BIOS prior to booting UZI.

- Add TTYASM.ASZ module for setting the TTY port parameters when changed.
  Currently, the serial port parameters (Data rate, # Bits, Parity, etc)
  are whatever was set prior to booting UZI.

- Polish the Floppy Driver (FLOPASM.ASZ) to add variable formats (auto-
  sense on mount?), local bufffering for 1k sector sizes, and accomodate
  disk formats with CP/M Boot Tracks.  The module is currently locked
  into a pseudo-MSDOS 1.44 MB High-Density 3.5" disk format.

- Refine the Emulator a bit and massage the addresses to provide full
  Environment space (512 bytes) and Argument area (512 bytes max).  Also
  add path default to a defined CP/M directory for accessing overlays,
  libraries, etc.
