# UZI180 for the P112

UZI180 is a unix-like operating system for Z180-based machines. Derived from UZI (a Unix Edition 7 lookalike system for Z80 machines), UZI180 further benefits from Z180-specific features like 1Mb addressing capabilities, internal timer-counter, etc. The original version of UZI180 from H. Bower was available from the TCJ web site (now gone). Mr Bower has also written an excellent introduction to UZI180, I have a copy online here.

The orginal UZI180, as posted on the TCJ, was a snapshot version of a work in progress. As such, it was far from stable and many features did not work properly. I've done many corrections and bug fixes to the sources that increased the overall stability of the system (it used to crash quite often with a "`Trap @ 0xXXXX`" message). The changes are summarized below:

Kernel:

- debugged the process dispatcher, most of the stability problems were related to process context-switching bugs.
- signals and pipes now work.
- added a /dev/proc pseudo-device as a way to communicate with the kernel in order to get system-related information, currently it is used only by the ps program to retrieve information about running processes.
- the embedded CP/M emulator is now working:

- 16-bit record values are used for file I/O operations, so file sizes are no longer limited to 32K.
- the emulator now issues a CALL (and not a JMP) to the application code, so programs that save and restore the stack pointer and return to CCP will no longer crash on exit.
- applications that directly access the BIOS now work: the emulator module was being wrongly compiled with jump optimization, which compromised the integrity of the BIOS JMP table as most of the jumps were replaced with a 2-byte relative jump instructions.
- found a workaround for those cases when an application opens a large number of files for read and never close them (e.g. the HiTech C compiler). That's perfectly legal in CP/M, but under UZI that causes file descriptor leaks and the emulator would quickly run out of resources.

- many other minor bug corrections.

Disk utilities:

- corrected a couple of serious bugs in fsck (present in the original UZI and also in UZI280), that actually caused it to corrupt an otherwise correct filesystem

Other:

- made a UZI version of the Hi-Tech C runtime library to be used by application programs
- many system utilities added
- added a boot loader, so now standalone bootable UZI floppies can be created.

For a full and detailed list of the changes, see the ChangeLog file.

Downloads

Source code for the kernel, precompiled binary
Source code for the system utilities, UZI180 binaries
Source code for the disk utilities, CP/M binaries, Linux binaries
Source code for the modified Hi-Tech C runtime library, binary
Bootable 3.5" 1.44Mb disk image (for best performance copy it into a floppy disk formatted under CP/M)

## How to create a UZI floppy

Under CP/M, you can just use a batch file similar to this. In case you prefer to do the whole thing manually, below are step-by-step instructions. Assuming you have a two 3.5"-floppy drive system, insert a CP/M disk with the UZI utilities on A: and a newly formatted floppy on B: (UZI's device 2), then follow these steps (user entered commands are shown in boldface):

First, we create the UZI filesystem with the mkfs (make filesystem) command:

> `A>mkfs 2 50 2880`

Here, '2' tells mkfs to create the filesystem on device number 2 (our '`fd1`', see dev_tab in config.h), '50' is the size of the system area (boot block + superblock + inode table), and '2880' is the size of the disk in blocks (one block is 512 bytes). Why 50? Well, that gives us 48 blocks for the inode table, and since an inode takes 64 bytes, we'll have a total of 384 inodes. Inodes are much like the data part of the CP/M directory entries, there is just one inode per file or directory, but unlike CP/M directory entries, a large file does not take several inodes. With 384 inodes you can have a maximum of 384 files and/or directories, which seems to be enough for a 1.44Mb floppy.

We can then use the fsck (filesystem check) utility to check the just created filesystem:

> `A>fsck 2`

As before, '2' refers to the device number 2. Once finished, assuming that no errors were found, we can start the interactive ucp utility in order to create the directory structure and to import files:

> `A>ucp 2`
> `ucp:`

Here 'ucp:' is, as you would have probably guessed, ucp's prompt. For a list of available commands you can enter 'help' or just '?'. You'll probably notice that the command syntax closely resembles unix commands. We begin by creating some directories, like the ones usually found in standard unix systems:

> `ucp: mkdir dev`
> `ucp: mkdir bin`
> `ucp: mkdir etc`
> `ucp: mkdir lib`
> `ucp: mkdir usr`
> `ucp: mkdir usr/bin`
> `ucp: mkdir root`
> `ucp: mkdir home`
> `ucp: mkdir tmp`

Next, we go to the /dev directory and create entries for our devices:

> `ucp: mknod tty1 20666 5`
> `ucp: mknod fd0 60644 1`
> `ucp: mknod fd1 60644 2`
> `ucp: mknod null 20622 6`
> `ucp: mknod kmem 20622 7`

Here, the first argument represents the device name (tty1 for the terminal, fd0 and fd1 for the floppy drives, null for the null devive, and kmem for a special "kernel memory" device). The second argument is the device modes and permission, in octal, 20666 is interpreted as "character device" (20000) and readable and writable by anybody (00666); see the unix.h file included with the kernel sources for more details about mode bits. The third argument is the device number, that should correspond to the numbers defined in dev_tab in config.h.

Now we proceed to copy some files. First we need to put the init program in the root directory:

> `ucp: cd /`
> `ucp: bget init`
> `ucp: chmod init 755`

The second argument to the chmod command is the file permissions. In this case we are making the file executable and readable by anyone (00755). Similary, we populate the /bin directory with utilities:

> `ucp: cd /bin`
> `ucp: bget ssh`
> `ucp: chmod ssh 775`
> `ucp: bget ls`
> `ucp: chmod ls 755`
> `ucp: bget cp`
> `ucp: chmod bget 755`
> `... etc ...`

Which and how many utilities you put in the /bin directory is entirely up to you. However, it is recommended to have a minimum set of standard utilities in order to have a comfortable working environment. You should always have at least a shell (in our case ssh - the simple shell). Other utilities include ls, pwd, cp, mv, rm, chmod, mkdir, rmdir, mknod, cat, mount and umount. Some shells already have builtin versions of these most used commands, but unfortunatelly not our tiny ssh. If you have enough disk space you can also add df, dd, ed, chown, chgrp, date, echo, kill, ps, sync, touch, more, grep, etc... It is recommended to place the system utilities in /bin and application programs in /usr/bin.

Don't forget to put a suitable passwd file in the /etc directory, otherwise you will not be able to login. Initially the passwd file would contain just a single line:

> `root::0:0:superuser:/root:/bin/ssh`

The init utility supplied understands the standard unix passwd file format, and supports encrypted passwords. Likewise, you'll want to have a group file as well. The passwd and group files are used by some other utilities, like ls, in order to display user and group names instead of numeric values. A minimalist group file would contain just a single line:

> `root::0:root`

Once you're finished copying files and making/removing directories you might want to boot your UZI system. Just quit from the ucp program,

> `ucp: quit`
> `A>`

optionally check the disk again using fsck as explained above and, keeping the UZI disk in B:, start the kernel. At the boot prompt answer '2' (device number 2, B: in CP/M terms):

> `A>uzi`
> `UZI180 version 1.5.2 built Sat Dec 7 19:25:31 CET 2002`
> `Copyright (c) 1998-2002 by H.F.Bower, D.Braun, S.Nitschke, H.Peraza`
> `1024kB total RAM, 960kB available to processes (15 processes max)`
> `boot: 2`
> `Mounting root fs: ok`
> `init version 0.8`

> `Welcome to UZI180 on /dev/tty1 (Z180)`

> `login:`

If everything went OK (i.e., your disk contained no filesystem errors, and the /init and /bin/ssh programs were present), you should get the login prompt. Login as root, hit enter at the password prompt, and you should get the shell command prompt:

> `login: root`
> `Password:`

> `ssh #`

You can now try to run some programs, say 'ls' or 'ls -al', 'date', etc...

These same steps can be used to prepare an UZI partition on a hard disk. You'll need to change the device number 2 in the mkfs and fsck commands to the appropiate device number for the particular hard disk partition. UZI does not recognizes any hard disk partition tables at this stage, so don't forget to use a kernel and utilities compiled with the appropiate partition bounds! For the mkfs command, you'll need in addition to increase the number of inodes, and to specify the correct number of partition blocks.

Too complicated? Well, there is an easier way: get a ready UZI180 disk image for a 3.5", 1.44Mb floppy. You can use the rawrite.exe program under MS-DOS or the dd command under Unix to transfer the image to a floppy. For best performance format the disk first under CP/M, so the sectors will have the optimum interleave value for the P112 board. The floppy is bootable with a suitable kernel, so you should not need CP/M at all. Just insert the floppy into drive A, reset the P112 and at the UZI's login prompt enter root and paswword root. Note that this time the kernel will not ask for a device to boot. The kernel was compiled for a board with a 16MHz CPU, so if you use a different clock frequency you'll end up with different timings.

## Using UZI's CP/M emulator:

One of the most attractive features of the UZI operating system is that it contains a built-in CP/M 2.2 emulator. It allows you to run existing CP/M programs like compilers, assemblers and editors directly from the shell command prompt.

Before executing a program, the kernel first tries to determine whether it is a native UZI application or a CP/M program by reading the first few bytes. UZI programs have an embedded signature, while CP/M programs usually begin with a JMP command (0C3h opcode). If that is not the case, then file must be patched. Here is the source for an utility to patch CP/M files that puts a JMP command at the beginning (UZI binary here). This utility could be compiled as well under CP/M (or MS-DOS, or Unix) with minor modifications.

## Boot Loader for UZI180

The block 0 on the UZI filesystem is the boot block, and is reserved for the bootloader code. However, existing UZI implementations rely on CP/M in order to boot the system: the kernel is started as a normal CP/M application and then it takes control over the system. It would be more desirable, however, to have standalone bootable disks.

Here is a simple UZI boot loader for the P112. It consists of the following: the boot program code and a boot installer.

How to use it: once you compiled your kernel, copy it to your UZI floppy disk (the location and name is not important), and then copy both the boot installer and the boot loader (the insboot and loader files respectively) to some directory, both of them must reside in the same directory. Make sure insboot has execute permissions (chmod 0755 insboot). Start UZI if you're not already running it, then invoke insboot specifying the name of the device to be made bootable and the full path to the kernel binary file.

For example, suppose your kernel is named vmuzi and is located in the root directory, and the boot utilities reside in the /boot directory, the command sequence will be the following:

> `ssh# cd /boot`
> `ssh# ./insboot /dev/fd0 /vmuzi 1`
> `insboot: boot sector installed`
> `ssh#`

The last parameter in the example above is optional, and specifies the drive number from which the root filesystem should be mounted (i.e. the value you would usually type in response to the kernel's "boot:" prompt). If the parameter is specified, the kernel will not prompt for the filesystem to boot.

Here is how the loader works: the boot installer program first reads the block allocation map of the kernel file, then writes to the boot block of the specified device the boot code from the 'loader' binary file and appends the block allocation map. When the system is booted, the loader uses the block allocation map to figure out where the kernel resides, then proceeds to load it block by block into memory starting from address 100h, and finally passes the control to it.

With this scheme it does not matter where the kernel is physically located or whether it is fragmented over the disk or not. You should, however, be aware that the insboot program must be run again every time the location or size of the kernel file changes, for example:

- if you replaced the kernel file with a new version, even if you copied it to the same place and using the same name.
- if you copied the kernel file to another directory and then erased the original.

In the other hand, insboot does not need to be re-run:

- if you used the mv command to rename the kernel file or to move it from one directory to another.

Note that the simple boot loader suupplied will work only with the P112, since it uses the ROM monitor disk services. However, the whole idea can be applied to any other system as well, you'll just have to write a suitable loader program and to modify the installer appropiately. In those cases where the boot code is so large that the kernel allocation map would not fit, you can use the following workaround: save the kernel allocation map into a separate file instead (that file will occupy just a single block), and pass that block number to the bootloader. During boot, the bootloader would first fetch the block containing the kernel allocation map, and then use it to load the kernel.

Last modified 7-Dec-2002
Hector Peraza <peraza@uia.ua.ac.be>