1 Introduction
2 Ground Rules

Building a File System
3 File Systems
4 File Content Data Structure
5 Allocation Cluster Manager
6 Exceptions and Emancipation
7 Base Classes, Testing, and More
8 File Meta Data
9 Native File Class
10 Our File System
11 Allocation Table
12 File System Support Code
13 Initializing the File System
14 Contiguous Files
15 Rebuilding the File System
16 Native File System Support Methods
17 Lookups, Wildcards, and Unicode, Oh My
18 Finishing the File System Class

The Init Program
19 Hardware Abstraction and UOS Architecture
20 Init Command Mode
21 Using Our File System
22 Hardware and Device Lists
23 Fun with Stores: Partitions
24 Fun with Stores: RAID
25 Fun with Stores: RAM Disks
26 Init wrap-up

The Executive
27 Overview of The Executive
28 Starting the Kernel
29 The Kernel
30 Making a Store Bootable
31 The MMC
32 The HMC
33 Loading the components
34 Using the File Processor
35 Symbols and the SSC
36 The File Processor and Device Management
37 The File Processor and File System Management
38 Finishing Executive Startup

Users and Security
39 Introduction to Users and Security
40 More Fun With Stores: File Heaps
41 File Heaps, part 2
42 SysUAF
43 TUser
44 SysUAF API

UCL
45 Shells and UCL

Glossary/Index


Download sources
Download binaries

Hardware and Device Lists

In this article, we are going to look at the hardware options. Here is the code in the main input list:
if( Match( 'HARDWARE', S, 3 ) ) then
begin
    Do_Hardware( S1 ) ;
end else

Here is the Do_Hardware function that serves as the input loop for hardware mode:

function Do_Hardware( S : string ) : boolean ;

var Dummy : integer ;
    S1 : string ;
    Prompt : boolean ;

begin
    Result := False ;
    Prompt := S = '' ;
    while( True ) do
    begin
        if( Prompt ) then
        begin
            Output( 'Hardware> ' ) ;
            S := Input( '' ) ;
        end ;
        if( S = #3 ) then
        begin
            Result := True ;
            exit ;
        end ;
        if( ( S = ESC ) or ( S = #26 ) ) then
        begin
            exit ;
        end ;
        S := Edit( S, 4 or 8 or 32 or 128 ) ;
        if( S = '' ) then
        begin
            continue ;
        end ;
        Dummy := pos( ' ', S + ' ' ) ;
        S1 := copy( S, Dummy + 1, length( S ) ) ;
        S := copy( S, 1, Dummy - 1 ) ;
        if( ( S = '?' ) or Match( 'HELP', S, 1 ) ) then
        begin
            Output_Line( 'DEVICE - Manage devices' ) ;
            Output_Line( 'HELP - Show this text' ) ;
            Output_Line( 'MEMORY - Manage memory' ) ;
            Output_Line( '' ) ;
        end else
        if( Match( 'DEVICE', S, 1 ) ) then
        begin
            if( Hardware_Device( S1 ) ) then
            begin
                exit ;
            end ;
        end else
        if( Match( 'MEMORY', S, 1 ) ) then
        begin
            if( Hardware_Memory( S1 ) ) then
            begin
                exit ;
            end ;
        end else
        if( S <> '' ) then
        begin
            Output_Line( 'Invalid Hardware command' ) ;
        end ;
        if( not Prompt ) then
        begin
            exit ;
        end ;
    end ; // while( True )
end ; // Do_Hardware

For now, we will support managing devices and memory.

Here is the input loop for the Hardware memory command loop:

function Hardware_Memory( S : string ) : boolean ;

var Dummy : integer ;
    S1 : string ;
    Prompt : boolean ;

begin
    Result := False ;
    Prompt := S = '' ;
    while( True ) do
    begin
        if( Prompt ) then
        begin
            Output( 'Hardware memory> ' ) ;
            S := Input( '' ) ;
        end ;
        if( S = #3 ) then
        begin
            Result := True ;
            exit ;
        end ;
        if( ( S = ESC ) or ( S = #26 ) ) then
        begin
            exit ;
        end ;
        S := Edit( S, 4 or 8 or 32 or 128 ) ;
        if( S = '' ) then
        begin
            continue ;
        end ;
        Dummy := pos( ' ', S + ' ' ) ;
        S1 := copy( S, Dummy + 1, length( S ) ) ;
        S := copy( S, 1, Dummy - 1 ) ;
        if( ( S = '?' ) or Match( 'HELP', S, 1 ) ) then
        begin
            Output_Line( 'HELP - Show this text' ) ;
            Output_Line( 'LIST - List memory' ) ;
            Output_Line( '' ) ;
        end else
        if( Match( 'LIST', S, 1 ) ) then
        begin
            if( Hardware_Memory_List( S1 ) ) then
            begin
                exit ;
            end ;
        end else
        if( S <> '' ) then
        begin
            Output_Line( 'Invalid subcommand' ) ;
        end ;
        if( not Prompt ) then
        begin
            exit ;
        end ;
    end ; // while( True )
end ; // Hardware_Memory

At this point, we only support the LIST sub-command.

The Memory list simply shows the layout of memory. On some computers, the available memory may not be contiguous, and some of it may be read-only. As a consequence, the HAL may return multiple segments of memory, each with its own range of addresses and characteristics. So this routine loops through all the segments of memory returned by the HAL and displays them.

function Hardware_Memory_List( S : string ) : boolean ;

var Index : integer ;
    Info : TMemory_Info ;
    Last, Size : int64 ;

begin // Hardware_Memory_List
    Result := False ;
    Last := 0 ;
    Index := 0 ;
    Info := HAL.Memory( Index ) ;
    while( Info.Memory_Type <> MT_Non_Existant ) do
    begin
        if( Info.Low > Last ) then
        begin
            S := Build_Range( Last, Info.Low - 1 ) ;
            Output_Line( 'NXT  ' + S ) ;
        end ;
        S := Build_Range( Info.Low, Info.High ) ;
        case Info.Memory_Type of
            MT_RAM : Output( 'RAM  ' ) ;
            MT_ROM : Output( 'ROM  ' ) ;
            MT_WOM : Output( 'WOM  ' ) ;
        end ; // case Info.Device_Type
        Size := Info.High - Info.Low + 1 ;
        Output( S ) ;
        S := Abbreviate_Size( Size ) ;
        Output_Line( '   ' + S ) ;
        Last := Info.High + 1 ;
        inc( Index ) ;
        Info := HAL.Memory( Index ) ;
    end ; // while( Info.Device_Type <> DT_Non_Existant )
end ; // Hardware_Memory_List

The HAL Memory method returns the following record:
type TMemory_Info = packed record
                        Memory_Type : word ; // See MT_*
                        Low : int64 ;
                        High : int64
                    end ;

The Memory type values are as follows:
MnuemonicValueDescription
MT_Non_Existant0Indicates the index is out of range
MT_RAM1R/W memory
MT_ROM2Read-only memory
MT_WOM3Write-only memory

The local Build_Range function formats memory ranges as strings. It shows the range of addresses in decimal and in the default base for the machine (if it differs from decimal). The HAL indicates the default base for the hardware. For instance, PDP-11s use a base of 8 (octal). Most modern hardware use a base of 16 (hexadecimal). When I say that the hardware "uses" a given base, it has to do with how the manufacturer's documentation represents data and address values.

Here is the input loop for the hardware device command.

function Hardware_Device( S : string ) : boolean ;

var Dummy : integer ;
    S1 : string ;
    Prompt : boolean ;

begin
    Result := False ;
    Prompt := S = '' ;
    while( True ) do
    begin
        if( Prompt ) then
        begin
            Output( 'Hardware device> ' ) ;
            S := Input( '' ) ;
        end ;
        if( S = #3 ) then
        begin
            Result := True ;
            exit ;
        end ;
        if( ( S = ESC ) or ( S = #26 ) ) then
        begin
            exit ;
        end ;
        S := Edit( S, 4 or 8 or 32 or 128 ) ;
        if( S = '' ) then
        begin
            continue ;
        end ;
        Dummy := pos( ' ', S + ' ' ) ;
        S1 := copy( S, Dummy + 1, length( S ) ) ;
        S := copy( S, 1, Dummy - 1 ) ;
        if( ( S = '?' ) or Match( 'HELP', S, 1 ) ) then
        begin
            Output_Line( 'HELP - Show this text' ) ;
            Output_Line( 'LIST - List devices' ) ;
            Output_Line( '' ) ;
        end else
        if( Match( 'LIST', S, 1 ) ) then
        begin
            if( Hardware_Device_List( S1 ) ) then
            begin
                exit ;
            end ;
        end else
        if( S <> '' ) then
        begin
            Output_Line( 'Invalid Hardware command' ) ;
        end ;
        if( not Prompt ) then
        begin
            exit ;
        end ;
    end ; // while( True )
end ; // Hardware_Device

For now, all we do is list the devices. Here is the device list code:
function Hardware_Device_List( S : string ) : boolean ;

var Index : integer ;
    Info : TDevice_Info ;
    Store : TCOM_Store64 ;

begin
    Result := False ;
    Index := 0 ;
    Info := HAL.Device( Index ) ;
    while( Info.Device_Type <> DT_Non_Existant ) do
    begin
        S := inttostr( Info.Device_Unit ) ;
        while( length( S ) < 3 ) do
        begin
            S := S + ' ' ;
        end ;
        S := chr( 65 + Info.Controller ) + S ;
        case Info.Device_Type of
            DT_Unknown : Output( '    ' + S + '   unknown  ' ) ;
            DT_Serial :
                begin
                    Output( 'TERM' + S + '   serial   ' ) ;
                end ;
            DT_Store :
                begin
                    Output( 'DISK' + S + '   store    ' ) ;
                    if( Info.Media_Present ) then
                    begin
                        Store := HAL.Store( Index ) ;
                        S := inttostr( Store.Max_Storage ) ;
                        while( length( S ) < 12 ) do
                        begin
                            S := ' ' + S ;
                        end ;
                        Output( S ) ;
                        if( Store.Read_Only ) then
                        begin
                            Output( '  Read-only' ) ;
                        end ;
                        if( Store.Write_Only ) then
                        begin
                            Output( '  Write-only' ) ;
                        end ;
                    end else
                    begin
                        Output( 'media not present' ) ;
                    end ;
                end ;
        end ; // case Info.Device_Type
        Output_Line( '' ) ;
        inc( Index ) ;
        Info := HAL.Device( Index ) ;
    end ; // while( Info.Device_Type <> DT_Non_Existant )
end ; // Hardware_Device_List

This loops through the HAL devices, as we did in the code we discussed in the previous article, and then displays the list.

Before moving on, we will go back to the disk commands and cover the disk CLONE command. This allows us to do a sector-by-sector copy from one disk to another.

function Disk_Clone( Command : string ) : boolean ;

var Dummy : integer ;
    Prompt : boolean ;
    Store : TCOM_Store64 ;
    S, S1 : string ;

begin // Disk_Clone
    Prompt := Command = '' ;
    Result := False ;
    while( true ) do
    begin
        // Query user for disk...
        if( Command = '' ) then
        begin
            Output( 'Disk clone (DISKA0) > ' ) ;
            S := Input( 'DISKA0' ) ;
        end else
        begin
            S := Command ;
        end ;
        if( S = #3 ) then
        begin
            Result := True ;
            exit ;
        end ;
        if( ( S = ESC ) or ( S = #26 ) ) then
        begin
            exit ;
        end ;
        S := Edit( S, 4 or 8 or 32 or 128 ) ;
        Dummy := pos( ' ', S + ' ' ) ;
        S1 := copy( S, Dummy + 1, length( S ) ) ;
        S := copy( S, 1, Dummy - 1 ) ;
        if( S = '' ) then
        begin
            continue ;
        end ;
        if( ( S = '?' ) or Match( 'HELP', S, 1 ) ) then
        begin
            Output_Line( '<disk1> <disk2> - source and target disks' ) ;
            Output_Line( 'HELP - Show this text' ) ;
            Output_Line( '' ) ;
        end else
        begin
            Store := Parse_Device( S ) ;
            if( Store <> nil ) then
            begin
                if( Disk_Clone_Device( Store, S, S1 ) ) then
                begin
                    Result := True ;
                    exit ;
                end ;
            end ;
        end ;
        if( not Prompt ) then
        begin
            exit ;
        end ;
    end ; // while( True )
end ; // Disk_Clone

This function is the main input loop for the clone command. It parses the source device and calls the actual clone routine.

function Disk_Clone_Device( Store : TCOM_Store64 ; St, Command : string ) : boolean ;

var Buffer : PChar ;
    Current, Max, Percent : int64 ;
    Index : TStore_Address64 ;
    Prompt : boolean ;
    S : string ;
    Saved : integer ;
    Siz, Typ : longint ;
    Store1 : TCOM_Store64 ;
    U : TUnified_Exception ;

begin // Disk_Clone
    Prompt := Command = '' ;
    Result := False ;
    while( true ) do
    begin
        // Query user for disk...
        if( Command = '' ) then
        begin
            Output( 'Disk clone ' + St + ' (DISKA1) > ' ) ;
            S := Input( 'DISKA1' ) ;
        end else
        begin
            S := Command ;
        end ;
        if( S = #3 ) then
        begin
            Result := True ;
            exit ;
        end ;
        if( ( S = ESC ) or ( S = #26 ) ) then
        begin
            exit ;
        end ;
        S := Edit( S, 4 or 8 or 32 or 128 ) ;
        if( S = '' ) then
        begin
            continue ;
        end ;
        if( ( S = '?' ) or Match( 'HELP', S, 1 ) ) then
        begin
            Output_Line( '<disk> - target disks' ) ;
            Output_Line( 'HELP - Show this text' ) ;
            Output_Line( '' ) ;
        end else
        begin
            Store1 := Parse_Device( S ) ;
            if( Store1 <> nil ) then
            begin
                if( Store = Store1 ) then
                begin
                    Output_Line( 'Cannot clone a disk to itself' ) ;
                end else
                if(
                    ( Store.Max_Storage <> Store1.Max_Storage )
                    or
                    ( Store.Min_Storage <> Store1.Min_Storage )
                  ) then
                begin
                    Output_Line( 'Incompatible disks' ) ;
                end else
                begin
                    Output( 'Any existing data on ' + S + ' will be lost.  Continue? <NO> ' ) ;
                    S := Input( 'NO' ) ;
                    if( copy( S, 1, 1 ) = 'Y' ) then
                    begin
                        Index := 0 ;
                        Max := Store.Max_Storage div Store.Min_Storage div 100 ; // 1% of the copy
                        Current := Max ;
                        Percent := 0 ;
                        Buffer := allocmem( Store.Min_Storage ) ;
                        try
                            while( Index < Store.Max_Storage ) do
                            begin
                                if( Keyboard.Peek = #3 ) then
                                begin
                                    Keyboard.Input_Line ; // Swallow the ^C
                                    Output_Line( '' ) ;
                                    Result := True ;
                                    exit ;
                                end ;
                                dec( Current ) ;
                                if( Current <= 0 ) then
                                begin
                                    Current := Max ;
                                    inc( Percent ) ;
                                    case Percent of
                                        10,20,30,40,50,60,70,80,90 : Output( inttostr( Percent ) + '%' ) ;
                                        5,15,25,35,45,55,65,75,85,95 : Output( '.' ) ;
                                    end ;
                                end ;
                                Store.Read_Data( Buffer^, Index, Store.Min_Storage, U ) ;
                                if( U <> nil ) then
                                begin
                                    Output_Line( U.Error_Text( Siz, Typ ) ) ;
                                end ;
                                Store1.Write_Data( Buffer^, Index, Store.Min_Storage, U ) ;
                                if( U <> nil ) then
                                begin
                                    Output_Line( U.Error_Text( Siz, Typ ) ) ;
                                end ;
                                Index := Index + Store.Min_Storage ;
                            end ; // while( Index < Store.Max_Storage )
                        finally
                            freemem( Buffer ) ;
                        end ;
                        Output_Line( '100%' ) ;
                    end ; // if( copy( S, 1, 1 ) = 'Y' )
                end ; // if
            end ; // if( Store1 <> nil )
        end ; // if
        if( not Prompt ) then
        begin
            exit ;
        end ;
    end ; // while( True )
end ; // Disk_Clone_Device

The function gets the target disk, and verifies that it is a valid disk. It then makes sure that the source and target disks are not the same. Finally, it makes sure that the disk sizes and sector sizes are identical (otherwise a clone is not possible). The process displays status as the copy occurs, which is especially useful for very large disks.

In the next article, we will discuss disk partitions.