NitrOS-9's Unified I/O System

From NitrOS-9
Revision as of 22:26, 6 April 2010 by Boisy (Talk | contribs) (Created page with 'NitrOS-9’s Unified Input/Output System Chapter 1 mentioned that NitrOS-9 has a unified I/O system, consisting of all modules except those at the kernel level. This chapter disc…')

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

NitrOS-9’s Unified Input/Output System Chapter 1 mentioned that NitrOS-9 has a unified I/O system, consisting of all modules except those at the kernel level. This chapter discusses the I/O modules in detail.

The VDG interface performs both interface and low-level routines for VDG Color Computer 2 compatible modes and has limited support for high resolution screen allocation.

The CoGrf interface provides the standard code interpretations and interface functions.

The CoWin interface, available in the Multi-View package, contains all the functionality of CoGrf along with additional support features. If you use CoWin, do not include Grflnt.

Both CoWin and Grflnt use the low-level driver GrfDrv to perform drawing on the bitmap screens.

Term_VDG uses VTIO/CoVDG while Term_win40/Term_win80 and all window descriptors use VTIO/(CoWin/CoVDG)/GrfDrv modules.

The I/O system provides system-wide, hardware-independent I/O services for user programs and OS-9 itself. All I/O system calls are received by the kernel and passed to the I/O manager for processing.

The I/O manager performs some processing, such as the allocation of data structures for the I/O path. Then, it calls the file managers and device drivers to do most of the work. Additional file manager, device driver, and device descriptor modules can be loaded into memory from files and used while the system is running. The I/O Manager The I/O manager provides the first level of service of I/O system calls. It routes data on I/O process paths to and from the appropriate file managers and device drivers.

The I/O Manager also maintains two important internal OS-9 data structures: the device table and the path table. Never modify the I/O manager.

When a path is opened, the I/O manager tries to link to a memory module that has the device name given or implied in the pathlist. This module is the device descriptor. It contains the names of the device driver and file manager for the device. The I/O manager saves the names so later system calls can be routed to these modules. File Managers NitrOS-9 can have any number of file manager modules. Each of these modules processes the raw data stream to or from a class of device drivers that have similar operational characteristics. It removes as many unique characteristics as possible from I/O operations. Thus, it assures that similar devices conform to the NitrOS-9 standard I/O and file structure.

The file manager also is responsible for mass storage allocation and directory processing, if these are applicable to the class of devices it serves. File managers usually buffer the data stream and issue requests to the kernel for dynamic allocation of buffer memory. They can also monitor and process the data stream, for example, adding linefeed characters after carriage-return characters.

The file managers are re-entrant. The three standard NitrOS-9 file managers are:

Random block file manager: The RBF manager supports random-access, block-structured devices such as disk systems and bubble memories. (Chapter 5 discusses the RBF manager in detail.)

Sequential Character File Manager: The SCF manager supports single-character-oriented devices, such as CRTs or hardcopy terminals, printers, and modems. (Chapter 6 discusses SCF in detail.)

Pipe File Manager (PIPEMAN): The pipe manager supports interprocess communication via pipes. File Manager Structure Every file manager must have a branch table in exactly the following format. Routines that are not used by the file manager must branch to an error routine that sets the carry and loads B with an appropriate error code before returning. Routines returning without error must ensure that the carry bit is clear.

  • All routines are entered with:
  • (Y) = Path Descriptor pointer
  • (U) = Caller’s register stack pointer

EntryPt equ * lbra Create lbra Open lbra MakDir lbra ChgDir lbra Delete lbra Seek lbra Read lbra Write lbra ReadLn lbra WriteLn lbra GetStat lbra PutStat lbra Close

Create, Open Create and Open handle file creating and opening for devices. Typically, the process involves allocating any required buffers, initializing path descriptor variables, and establishing the path name. If the file manager controls multi-file devices (RBF), directory searching is performed to find or create the specified file. MakDir MakDir creates a directory file on multi-file devices. MakDir is neither preceded by a Create nor followed by a Close. File managers that are incapable of supporting directories need to return carry set with an appropriate error code in Register B. ChgDir On multi-file devices, ChgDir searches for a directory file. If ChgDir finds the directory, it saves the address of the directory (up to four bytes) in the caller’s process descriptor. The descriptor is located at P$DIO + 2 (for a data directory) or P$DIO + 8 (for an execution directory).

In the case of the RBF manager, the address of the directory’s file descriptor is saved. Open/Create begins searching in the current directory when the caller’s pathlist does not begin with a slash. File managers that do not support directories should return the carry set and an appropriate error code in Register B. Delete Multi-file device managers handle file delete requests by initiating a directory search that is similar to Open. Once a device manager finds the file, it removes the file from the directory.

Any media in use by the file are returned to unused status. In the case of the RBF manager, space is returned for system use and is marked as available in the free cluster bitmap on the disk. File managers that do not support multi-file devices return an error. Seek File managers that support random access devices use Seek to position file pointers of an already open path to the byte specified. Typically, the positioning is a logical movement. No error is produced at the time of the seek if the position is beyond the current “end of file.”

Normally, file managers that do not support random access ignore Seek, However, an SCF-type manager can use Seek to perform cursor positioning. Read Read returns the number of bytes requested to the user’s data buffer. Make sure Read returns an EOF error if there is no data available. Read must be capable of copying pure binary data, and generally performs no editing on the data. Generally, the file manager calls the device driver to actually read the data into the buffer. Then, the file manager copies the data from the buffer into the user’s data area to keep file managers device independent. Write The Write request, like Read, must be capable of recording pure binary data without alteration. The routines for Read and Write are almost identical with the exception that Write uses the device driver’s output routine instead of the input routine. The RBF manager and similar random access devices that use fixed length records (sectors) must often pre-read a sector before writing it, unless they are writing the entire sector. In OS-9, writing past the end of file on a device expands the file with new data. ReadLn ReadLn differs from Read in two respects. First, ReadLn terminates when the first end-of-line (carriage return) is encountered. ReadLn performs any input editing that is appropriate for the device. In the case of SCF, editing involves handling functions such as backspace, line deletion, and the removal of the high order bit from characters. WriteLn WriteLn is the counterpart of ReadLn. It calls the device driver to transfer data up to and including the first (if any) carriage return encountered. Appropriate output editing can also be performed. For example, SCF outputs a line feed, a carriage return character, and nulls (if appropriate for the device). It also pauses at the end of a screen page. GetStat, PutStat The GetStat (get status) and PutStat (put status) system calls are wildcard calls designed to provide a method of accessing features of a device (or file manager) that are not generally device independent. The file manager can perform specific functions such as setting the size of a file to a given value. Pass unknown status calls to the driver to provide further means of device independence. For example, a PutStat call to format a disk track might behave differently on different types of disk controllers. Close Close is responsible for ensuring that any output to a device is completed. (If necessary, Close writes out the last buffer.) It releases any buffer space allocated in an Open or Create. Close does not execute the device driver’s terminate routine, but can do specific end-of-file processing if you want it to, such as writing end-of-file records on disks, or form feeds on printers. Interfacing with Device Drivers Strictly speaking, device drivers must conform to the general format presented in this manual. The I/O Manager is slightly different because it only uses the Init and Terminate entry points.

Other entry points need only be compatible with the file manager for which the driver is written. For example, the Read entry point of an SCF driver is expected to return one byte from the device. The Read entry point of an RBF driver, on the other hand, expects Read to return an entire sector.

The following code is part of an SCF file manager. The code shows how a file manager might call a driver.

  • IOEXEC
  • Execute Device's Read/Write Routine
  • Passed: (A) = Output character (write)
  • (X) = Device Table entry ptr
  • (Y) = Path Descriptor pointer
  • (U) = Offset of routine (D$Read,D$Write)
  • Returns: (A) = Input char (read)
  • (B) = Error code, CC set if error
  • Destroys B,CC

IOEXEC pshs a,x,y,u save registers ldu V$STAT,x get static storage for driver ldx V$DRIV,x get driver module address ldd M$EXEC,x and offset of execution entries addd 5,s offset by read/write leax d,x absolute entry address lda ,s+ restore char (for write) jsr ,x execute driver read/write puls x,y,u,pc return (A)=char, (B)=error emod Module CRC Size equ * size of sequential file manager


Device Driver Modules The device driver modules are subroutine packages that perform basic, low-level I/O transfers to or from a specific type of I/O device hardware controller. These modules are re-entrant. So, one copy of the module can concurrently run several devices that use identical I/O controllers.

Device driver modules use a standard module header, in which the module type is specified as code $Ex (device driver). The execution offset address in the module header points to a branch table that has a minimum of six 3-byte entries.

Each entry is typically an LBRA to the corresponding subroutine. The file managers call specific routines in the device driver through this table, passing a pointer to a path descriptor and passing the hardware control register address in the 6809 registers. The branch table looks like this:

Code Meaning $00 Device initialization routine $03 Read form device $06 Write to device $09 Get device status $0C Set device status $0F Device termination routine

(For a complete description of the parameters passed to these subroutines, see the “Device Driver Subroutines” sections in Chapters 5 and 6.) �
















�NitrOS-9 Interaction with Devices Device drivers often must wait for hardware to complete a task or for a user to enter data. Such a wait situation occurs if an SCF device driver receives a Read but there is no data is available, or if it receives a Write and no buffer space is available. NitrOS-9 drivers that encounter this situation should suspend the current process (via F$Sleep). In this way the driver allows other processes to continue using CPU time.

The most efficient way for a driver to awaken itself and resume processing data is by using interrupt requests (IRQs). It is possible for the driver to sleep for a number of system clock ticks and then check the device or buffer for a ready signal. The drawbacks to this technique are:

It requires the system clock to always remain active.

It might require a large number of ticks (perhaps 20) for the device to become ready. Such a case leaves you with a dilemma. If you make the program sleep for two ticks, the system wastes CPU time while checking for device ready. If the driver sleeps 20 ticks, it does not have a good response time.

An interrupt system allows the hardware to report to the CPU and the device drivers when the device is finished with an operation. Using interrupts to its advantage, a device driver can setup interrupt handling to occur when a character is sent or received or when a disk operation is complete. There is a built-in polling facility for pausing and awakening processes. Here is a technique for handling interrupts in a device driver:

Use the Init routine to place the driver interrupt service call (IRQSVC) routine in the IRQ polling sequence via an F$IRQ system call:


ldd V.Port,u get address to poll leax IRQPOLL,pcr point to IRQ packet leay IRQSERVC,pcr point to IRQ routine os9 F$IRQ add dev to poll Sequence bcs Error abnormal exit if error


Ensure that driver programs waiting for their hardware call the sleep routine. The sleep routine copies V.Busy to V.Wake. Then, it goes to sleep for some period of time.

When the driver program wakes up, have it check to see whether it was awakened by an interrupt or by a signal sent from some other process.


Usually, the driver performs this check by reading the V.Wake storage byte. The V.Busy byte is maintained by the file manager to be used as the process ID of the process using the driver. When V.Busy is copied into V.Wake, then V.Wake becomes a flag byte and an information byte. A non-zero Wake byte indicates that there is a process awaiting an interrupt. The value in the Wake byte indicates the process to be awakened by sending a wakeup signal as shown in the following code:

lda V.Busy,u get proc ID sta V.Wake,u arrange for wakeup andcc #^IntMasks prep for interrupts Sleep50 ldx #0 or any other tick time (if signal test ) OS9 F$Sleep await an IRQ ldx D.Proc get proc desc ptr if signal test ldb P$Signal,x i5 signal present? (if signal test) bne SigTest bra if 50 if Signal test tst V.Wake,u IRQ occur? bne Sleep50 bra if not

Note that the code labeled “if signal test” is only necessary if the driver wishes to return to the caller if a signal is sent without waiting for the device to finish. Also note that IRQs and FIRQs must be masked between the time a command is given to the device and the moving of V.Busy and V .Wake. If they are not masked, it is possible for the device IRQ to occur and the IRQSERVC routine to become confused as to whether it is sending a wakeup signal or not.

When the device issues an interrupt, NitrOS-9 calls the routine at the address given in F$IRQ with the interrupts masked. Make the routine as short as possible, and have it return with an RTS instruction. IRQSERVC can verify that an interrupt has occurred for the device. It needs to clear the interrupt to retrieve any data in the device. Then the V.Wake byte communicates with the main driver module. If V.Wake is non-zero, clear it to indicate a true device interrupt and use its contents as the process ID for an F$Send system call. The F$Send call sends a wakeup signal to the process. Here is an example:


ldx V.Port,u get device address tst ?? is it real interrupt from device? bne IRQSVC90 bra to error if not lda Data,x get data from device sta 0,y lda V.Wake,u beq IRQSVC80 bra if none clr V.Wake,u clear it as flag to main routine ldb #S$Wake,u get wakeup signal os9 F$Send Send Signal to driver IRQSVC80 clrb clear carry bit (all is well) rts IROSVC90 comb Set carry bit (is an IRQ call) rts

Suspend State (NitrOS-9 Level 2 only)

The Suspend State allows the elimination of the F$Send system call during interrupt handling. Because the process is already in the active queue, it need not be moved from one queue to another. The device driver IRQSERVC routine can now wake up the suspended main driver by clearing the process status byte suspend bit in the process state. Following are sample routines for the Sleep and IRQSERVC calls:

lda D.Proc get process ptr sta V.Wake,u prep for re-awakening enable device to IRQ, give command, etc. bra Cmd5O enter suspend loop Cmd30 ldx D.Proc get ptr to process desc lda P$State,x get state flag ora Suspend put proc in suspend state sta P$State,x save it in proc desc andcc #^IntMasks unmask interrupts ldx #1 give up time slice OS9 F$Sleep suspend (in active queue) Cmd50 orcc #IntMasks mask interrupts while changing state ldx D.Proc get proc desc addr (if signal test) lda P$Signal,x get signal (if signal test) beq SigProc bra if signal to be handled lda V.Wake,u true interrupt? bne Cmd30 bra if not andcc #^IntMasks assure interrupts unmasked

Note that D.Proc is a pointer to the process descriptor of the current process. Process descriptors are always allocated on 256 byte page boundaries. Thus, having the high order byte of the address is adequate to locate the descriptor. D.Proc is put in V.Wake as a dual value. In one instance, it is a flag byte indicating that a process is indeed suspended. In the other instance, it is a pointer to the process descriptor which enables the IRQSERVC routine to clear the suspend bit. It is necessary to have the interrupts masked from the time the device is enabled until the suspend bit has been set. Making the interrupts ensure that the IRQSERVC routine does not think it has cleared the suspend bit before it is even set. If this happens, when the bit is set the process might go into permanent suspension. The IRQSERVC routine sample follows:

ldy V.Port,u get dev addr tst V.Wake,u is process awaiting IRQ? beq IRQSVCER no exit clear device interrupt exit if IRQ not from this device lda V.Wake,u get process ptr clrb stb V.Wake,u clear proc waiting flag tfr d,x get process descriptor ptr lda P$State,x get state flag anda # Suspend clear suspend state sta P$State,x save it clrb clear carry bit rts IRQSVCER comb Set carry bit rts Device Descriptor Modules Device descriptor modules are small, non-executable modules. Each one provides information that associates a specific I/O device with its logical name, hardware controller address(es), device driver, file manager name, and initialization parameters.

Unlike the device drivers and file managers, which operate on classes of devices, each device descriptor tailors its functions to a specific device. Each device must have a device descriptor.

Device descriptor modules use a standard module header, in which the module type is specified as code $Fx (device descriptor). The name of the module is the name by which the system and user know the device (the device name given in path lists).

The rest of the device descriptor header consists of the information in the following chart:


Relative Address(es) Use $09,$0A The relative address of the file manager name string address $0B,$0C The relative address of the device driver name string $0D Mode/Capabilities: D S PE PW PR E W R (directory, single user, public execute, public write, public read, execute, write, read) $0E,$0F,$10 The absolute physical (24-bit) address of the device controller $11 The number of bytes (n bytes) in the initialization table $12,$12 + n Initialization table

When OS-9 opens a path to the device, the system copies the initialization table into the option section (PD.OPT) of the path descriptor. (See “Path Descriptors” in this chapter.)

The values in this table can be used to define the operating parameters that are alterable by the Get Status and Set Status system calls (I$GetStt and I$SetStt). For example, parameters that are used when initializing terminals define which control characters are to be used for functions such as backspace and delete.

The initialization table can be a maximum of 32 bytes long. If the table is fewer than 32 bytes long, OS-9 sets the remaining values in the path descriptor to 0.

You might wish to add devices to your system. If a similar device driver already exists, all you need to do is add the new hardware and load another device descriptor. Device descriptors can be in the boot module or they can be loaded into RAM from mass-storage files while the system is running.

The following diagram illustrates the device descriptor format:

Device Descriptor Format Name "Relative Address" Bytes Use M$ID $00-$01 2 Sync Bytes ($87CD) M$Size $02-$03 2 Module Size (bytes) M$Name $04-$05 2 Offset to Module Name M$Type $06 1 Type / Language M$Revs $07 1 Attributes / Revision Level M$Parity $08 1 Header Parity Check M$FMgr $09-$0A 2 File Manager Name Offset M$PDev $0B-$0C 2 Device Driver Name Offset M$Mode $0D 1 Mode M$Port $0E-$10 3 Port Address M$Opt $11 1 Initialization Table Size $12,$12…n n Initialization table Name Strings, and so on CRC Check Value

Path Descriptors Every open path is represented by a data structure called a path descriptor (PD). The PD contains the information the file managers and device drivers require to perform I/O functions.

PDs are 64 bytes long and are dynamically allocated and deallocated by the I/O manager as paths are opened and closed.

They are internal data structures that are not normally referenced from user or applications programs. The description of PDs is presented here mainly for those programmers who need to write custom file managers, device drivers, or other extensions to OS-9.

PDs have three sections. The first section, which is ten bytes long, is the same for all file managers and device drivers. The information in the first section is shown in the following chart.

Path Descriptor: Standard Information

Name Address Bytes Use PD.PD $00 1 Path number PD.MOD $01 1 Access mode: 1 = read, 2 = write, 3 = update PD.CNT $02 1 Number of open paths using this PD PD.DEV $03 2 Address of the associated device table entry PD.CPR $05 1 Current process ID PD.RGS $06 2 Address of the caller’s register stack PD.BUF $08 2 Address of the 256-byte data buffer (if used) PD.FST $0A 22 Defined by the file manager PD.OPT $20 32 Reserved for the GetStat/SetStat options

PD.FST is 12-byte storage reserved for and defined by each type of file manager for file pointers, permanent variables, and so on.

PD.OPT is a 32-byte option area used for file or device operating parameters that are dynamically alterable. When the path is opened, the I/O manager initializes these variables by copying the initialization table that is in the device descriptor module. User programs can change the values later, using the Get Status and Set Status system calls.

PD.FST and PD.OPT are defined for the file manager in the assembly-language equate file (SCFDefs for the SCF manager or RBFDefs for the RBF manager). Random Block File Manager The random block file manager (RBF manager) supports disk storage. It is a re-entrant subroutine package called by the I/O manager for I/O system calls to random-access devices. It maintains the logical and physical file structures.

During normal operation, the RBF manager requests allocation and deallocation of 256-byte data buffers. Usually, one buffer is required for each open file. When physical I/O functions are necessary, the RBF manager directly calls the subroutines in the associated device drivers. All data transfers are performed using 256-byte data blocks (pages).

The RBF manager does not deal directly with physical addresses such as tracks and cylinders. Instead, it passes to the device drivers address parameters, using a standard address called a logical sector number, or LSN. LSNs are integers from 0 to n-1, where n is the maximum number of sectors on the media. The driver translates the logical sector number to actual cylinder/track/sector values.

Because the RBF manager supports many devices that have different performance and storage capacities, it is highly parameter-driven. They physical parameters it uses are stored on the media itself.

On disk systems, the parameters are written on the first few sectors of Track 0. The device drivers also use the information, particularly the physical parameters stored on Sector 0. These parameters are written by the FORMAT program that initializes and tests the disk. Logical and Physical Disk Organization All disks used by NitrOS9 store basic information, file structure, and storage allocation information on these first few sectors.

LSN 0 is the identification sector. LSN 1 is the disk allocation map sector. LSN 2 marks the beginning of the disk’s root directory. The following section tells more about LSN 0 and LSN 1. Identification Sector (LSN 0) LSN 0 contains a description of the physical and logical characteristics of the disk. These characteristics are set by the FORMAT command program when the disk is initialized.

The following table gives the NitrOS9 mnemonic name, byte address, size, and description of each value stored in this LSN 0.

Name "Relative Address" "Size (Bytes)" Use DD.TOT $00 3 Number of sectors on disk DD.TKS $03 1 Track size (in sectors) DD.MAP $04 2 Number of bytes in the allocation bit map DD.BIT $06 2 Number of sectors per cluster DD.DIR $08 3 Starting sector of the root directory DD.OWN $0B 2 Owner’s user number DD.ATT $0D 1 Disk attributes DD.DSK $0E 2 Disk identification (for internal use) DD.FMT $10 1 Disk format, density, number of sides DD.SPT $11 2 Number of sectors per track DD.RES $13 2 Reserved for future use DD.BT $15 3 Starting sector of the bootstrap file DD.BSZ $18 2 Size of the bootstrap file (in bytes) DD.DAT $1A 5 Time of creation (Y:M:D:H:M) DD.NAM $1F 32 Volume name in which the last character has the most significant bit set DD.OPT $3F Path descriptor options

Disk Allocation Map Sector (LSN 1) LSN 1 contains the disk allocation map, which is created by FORMAT. This map shows which sectors are allocated to the files and which are free for future use.

Each bit in the allocation map represents a sector or cluster of sectors on the disk. If the bit is set, the sector is considered to be in use, defective, or non-existent. If the bit is cleared, the corresponding cluster is available. The allocation map usually starts at LSN 1. The number of sectors it requires varies according to how many bits are needed for the map. DD.MAP specifies the actual number of bytes used in the map.

Multiple sector allocation maps allow the number of sectors/cluster to be as small as possible for high volume media.

The FORMAT utility bases the size of the allocation map on the size and number of sectors per cluster.

The DD.MAP value in LSN 0 specifies the number of bytes (in LSN 1) that are used in the map.

Each bit in the disk allocation map corresponds to one sector cluster on the disk. The DD.BIT value in LSN 0 specifies the number of sectors per cluster. The number is an integral power of 2 (1, 2, 4, 8, 16, and so on).

If a cluster is available, the corresponding bit is cleared. If it is allocated, non-existent, or physically defective, the corresponding bit is set. Root Directory The file is the parent directory of all other files and directories on the disk. It is the directory accessed using the physical device name (such as /D1). Usually, it immediately follows the Allocation Map. The location of the root directory file descriptor is specified in DD.DIR. The root directory contains an entry for each file that resides in the directory, including other directories. File Descriptor Sector The first sector of every file is the file descriptor. It contains the logical and physical description of the file.







The following table describes the contents of the file descriptor.

Name "Relative Address" "Size (Bytes)" Use FD.ATT $00 1 File attributes: D S PE PW PR E W R (see next chart) FD.OWN $01 2 Owner’s user ID FD.DAT $03 5 Date last modified (Y M D H M) FD.LNK $08 1 Link count FD.SIZ $09 4 File size (number of bytes) FD.CREAT $0D 3 Date created (Y M D) FD.SEG $10 240 Segment list (see next chart)

FD.ATT. The attribute byte contains the file permission bits. When set the bits indicate the following

Bit 7 Directory Bit 6 Single user Bit 5 Public execute Bit 4 Public write Bit 3 Public read Bit 2 Execute Bit 1 Write Bit 0 Read

FD.SEG. The segment list consists of a maximum of 48 5-byte entries that have the size and address of each file block in logical order. Each entry has the block’s 3-byte LSN and 2-byte size (in sectors). The entry following the last segment is zero.

After creation, a file has no data segments allocated to it until the first write. (Write operations past the current end-of-file cause sectors to be added to the file. The first write is always past the end-of-file.)

If the file has no segments, it is given an initial segment. Usually, this segment has the number of sectors specified by the minimum allocation entry in the device descriptor. If, however, the number of sectors requested is more than the minimum, the initial segment has the requested number.

Later expansions of the file usually are also made in minimum allocation increments. Whenever possible, NitrOS9 expands the last segment instead of adding a segment. When the file is closed, NitrOS9 truncates unused sectors in the last segment.

NitrOS9 tries to minimize the number of storage segments used in a file. In fact, many files have only one segment. In such cases, no extra read operations are needed to randomly access any byte in the file.

If a file is repeatedly closed, opened, and expanded, it can become fragmented so that it has many segments. You can avoid this fragmentation by writing a byte at the highest address you want to be used on a file. Do this before writing any other data. Directory Disk directories are files that have the D attribute set. A directory contains an integral number of entries, each of which can hold the name and LSN of a file or another directory.

Each directory entry contains 29 bytes for the filename followed by three bytes for the LSN of the file’s descriptor sector. The filename is left-justified in the field with the most significant bit of the last character set. Unused entries have a zero byte in the first filename character position.

Every disk has a master directory called the root directory. The DD.DIR value in LSN 0 (identification sector) specifies the starting sector of the root directory. The RBF Manager Definitions of the Path Descriptor As stated earlier in this chapter, the PD.FST section of the path descriptor is reserved for and defined by the file manager. The following table describes the use of this section by the RBF manager. For your convenience, it also includes the other sections of the PD.

Name "Relative Address" "Size (Bytes)" Use Universal Section (Same for all file managers and device drivers) PD.PD $00 1 Path number PD.MOD $01 1 "Access mode 1 = read 2 = write 3 = update" PD.CNT $02 1 Number of open images (paths using this PD) PD.DEV $03 2 Address of the associated device table entry PD.CPR $05 1 Current process ID PD.RGS $06 2 Address of the caller’s 6809 register stack PD.BUF $08 2 Address of the 256-byte data buffer (if used)

Name "Relative Address" "Size (Bytes)" Use The RBF manager Path Descriptor Definitions (PD.FST Section) PD.SMF $0A 1 "State flag: Bit 0 = current buffer is altered Bit 1 = current sector is in the buffer Bit 2 = descriptor sector is in the buffer" PD.CP $0B 4 Current logical file position (byte address) PD.SIZ $0F 4 File size PD.SBL $13 3 Segment beginning logical sector number (LSN) PD.SBP $16 3 Segment beginning physical sector number (PSN) PD.SSZ $19 3 Segment size PD.DSK $1C 2 Disk ID (for internal use only) PD.DTB $1E 2 Address of drive table

Name "Relative Address" "Size (Bytes)" Use "The RBF manager Option Section Definitions (PD.OPT Section) (Copied from the device descriptor)" PD.DTP $20 1 "Device class 0 = SCF 1 = RBF 2 = PIPE 3 = SBF" PD.DRV $21 1 Drive number (0..n) PD.STP $22 1 Step rate PD.TYP $23 1 Device type PD.DNS $24 1 Density capability PD.CYL $25 2 Number of cylinders (tracks) PD.SID $27 1 Number of sides (surfaces) PD.VFY $28 1 0 = verify disk writes PD.SCT $29 2 Default number of sectors per track PD.T0S $2B 2 Default number of sectors per track (Track 0) PD.ILV $2D 1 Sector interleave factor PD.SAS $2E 1 Segment allocation size PD.TFM $2F 1 DMA transfer mode PD.EXTEN $30 2 Path extension for record locking PD.STOFF $32 1 Sector/track offsets (Not copied from the device descriptor) PD.ATT $33 1 File attributes (D S PE PW PR E W R) PD.FD $34 3 File descriptor PSN PD.DFD $37 3 Directory file descriptor PSN PD.DCP $3A 4 File’s directory entry pointer PD.DVT $3E 2 Address of the device table entry

Any values not determined by this table default to zero. RBF-Type Device Descriptor Modules This section describes the use of the initialization table contained in the device descriptor modules for RBF-type devices. The following values are those the I/O manager copies from the device descriptor to the path descriptor.

Name "Relative Address" "Size (Bytes)" Use $00-$11 Standard device descriptor module header IT.DTP $12 1 "Device type: 0 = SCF 1 = RBF 2 = PIPE 3 = SBF" IT.DRV $13 1 Drive number IT.STP $14 1 Step rate IT.TYP $15 1 Device type (see RBF path descriptor) IT.DNS $16 1 "Media density: Always 1 (double) (see following information)" IT.CYL $17 2 Number of cylinders (tracks) IT.SID $19 1 Number of sides IT.VFY $1A 1 "0 = Verify disk writes 1 = no verify" IT.SCT $1B 2 Default number of sectors per track IT.T0S $1D 2 Default number of sectors per track (Track 0) IT.ILV $1F 1 Sector interleave factor IT.SAS $20 1 Minimum size of segment allocation (number of sectors to be allocated at one time) IT.DRV is used to associate a 1-byte integer with each drive that a controller handles. Number the drives for each controller as 0 to n-1, where n is the maximum number of drives the controller can handle.

IT.TYP specifies the device type (all types).

Bit 0 0 = 5-inch floppy diskette Bit 5 0 = Non-Color Computer format 1 = Color Computer format Bit 6 0 = Standard NitrOS9 format 1 = Non-standard format Bit 7 0 = Floppy diskette 1 = Hard disk

IT.DNS specifies the density capabilities (floppy diskette only).

Bit 0 0 = Single-bit density (FM) 1 = Double-bit density (MFM) Bit 1 0 = Single-track density (5-inch, 48 tracks per inch) 1 = Double-track density (5-inch, 96 tracks per inch)

IT.SAS specifies the minimum number of sectors allowed at one time. RBF Record Locking Record locking is a general term that refers to methods designed to preserve the integrity of files that can be accessed by more than one user or process. The NitrOS9 implementation of record locking is designed to be as invisible as possible. This means that existing programs do not have to be rewritten to take advantage of record locking facilities. You can usually write new programs without special concern for multi-user activity.

Record locking involves detecting and preventing conflicts during record access. Whenever a process modifies a record, the system locks out other procedures from accessing the file. It defers access to other procedures until it is safe for them to write to the record. The system does not lock records during reads; so, multiple processes can read the records at the same time. Record Locking and Unlocking To detect conflicts, NitrOS9 must recognize when a record is being updated. The RBF manager provides true record locking on a byte basis. A typical record update sequence is:

OS9 I$Read program reads record RECORD is LOCKED . . program updates record . OS9 I$Seek reposition to record OS9 I$Write record is rewritten RECORD IS RELEASED

When a file is opened in update mode, any read causes locking of the record being accessed. This happens because the RBF manager cannot determine in advance if the record is to be updated. The record stays locked until the next read, write, or close.

However, when a file is opened in the read or execute modes, the system does not lock accessed records because the records cannot be updated in these two modes.

A subtle but important problem exists for programs that interrogate a data base and occasionally update its data. If you neglect to release a record after accessing it, the record might be locked indefinitely. This problem is characteristic of record locking systems and you can avoid it with careful programming.

Only one potion of a file can be locked at a time. If an application requires more than one record to be locked, open multiple paths to the same file and lock the record accessed by each path. RBF notices that the same process owns both paths and keeps them from locking each other. Non-Shareable Files Sometimes (although rarely), you must create a file that can never be accessed by more than one user at a time. To lock the file, you set the single-user bit in the file’s attribute byte. You can do this by using the proper option when the file is created, or later using the NitrOS9 ATTR command. Once the single-user bit is set, only one use can open the file at a time. If other users attempt to open the file, Error 253 is returned. Note, however, that non-shareable means only one path can be opened to a file at one time. Do not allow two processes to concurrently access a non-shareable file through the same path.

More commonly, you need to declare a file as single-user only during the execution of a specific program. You can do this by opening the file with the single-user bit set. For example, suppose a process is sorting a file. With the file’s single-user bit set, NitrOS9 treats the file exactly as though it had a single-user attribute. If another process attempts to open the file, NitrOS9 returns Error 253.

You can duplicate non-shareable files by using the I$Dup system call. This means that it can be inherited and therefore accessible to more than one process at a time. Single-user means only that the file can be opened only once. End-of-File Lock A special case of record locking occurs when a user reads or writes data at the end of a file, creating an EOF Lock. An EOF Lock keeps the end of the file locked until a process performs a read or write that it is not at the end of the file. It prevents problems that might otherwise occur when two users want to simultaneously extend a file. The EOF Lock is the only case in which a write call automatically causes portions of a file to be locked. An interesting and useful side effect of the EOF Lock function occurs if a program creates a file for sequential output. As soon as the program creates the file, EOF Lock is set and no other process can pass the writer in processing the file. For example, if an assembler redirects a listing to a disk file, and a spooler utility tries to print a line from the file it is written, record locking makes the spooler wait and stay at least one step behind the assembler. Deadlock Detection A deadly embrace, or deadlock, typically occurs when two processes attempt to gain control of two or more disk areas at the same time. If each process gets one area (locking the other process), both processes become permanently stuck. Each waits for a segment that can never become free. This situation is not restricted to any particular record locking scheme or operating system.

When a deadly embrace occurs, RBF returns a deadlock error (Error 254) to the process that caused NitrOS9 to detect the deadlock. To avoid deadlocks, make sure that processes always access records of shared files in the same sequence.

When a deadlock error occurs, it is not sufficient for a program to retry the operation that caused the error. If all processes use this strategy, none can ever succeed. For any process to proceed, at least one must cancel operation to release control over a requesting segment. RBF-Type Device Driver Modules An RBF-type device driver module contains a package of subroutines that perform sector-oriented I/O to or from a specific hardware controller. Such a module is usually re-entrant. Because of this, one copy of one device driver module can simultaneously run several devices that use identical I/O controllers.

The I/O manager allocates a permanent memory area for each device driver. The size of the memory area is given in the device driver module header. The I/O manager and the RBF manager use some of this area. The device driver can use the rest in any manner. This area is used as follows: The RBF Device Memory Area Definitions Name "Relative Address" "Size (Bytes)" Use V.PAGE $00 1 Port extended address bits A20-A16 V.PORT $01 2 Device base address (defined by the I/O manager) V.LPRC $03 1 ID of the last active process (not used by RBF device drivers) V.BUSY $04 1 "ID of the current process using driver (defined by RBF) 0 = no current process" V.WAKE $05 1 ID of the process waiting for I/O completion (defined by the device driver) V.USER $06 0 Beginning of file manager specific storage V.NDRV $06 1 Maximum number of drives the controller can use (defined by the device driver) $07 8 Reserved DRVBEG $0F 0 Beginning of the drive tables TABLES $0F DRVMEM*N Space for number of tables reserved (n) FREE 0 Beginning of space available for driver

These values are defined in files in the DEFS directory on the Development Package disk.

TABLES. This area contains one table for each drive that the controller handles. (The RBF manager assumes that there are as many tables as indicated by V.NDRV.) Some time after the driver Init routine is called, the RBF manager issues a request for the driver to read LSN 0 from a drive table by copying the first part of LSN 0 (up to DD.SIZ) into the table. Following is the format of each drive table:


Name "Relative Address" "Size (Bytes)" Use DD.TOT $00 3 Number of sectors DD.TKS $03 1 Track size (in sectors) DD.MAP $04 2 Number of bytes in the allocation bit map DD.BIT $06 2 Number of sectors per bit (cluster size) DD.DIR $08 3 Address (LSN) of the root directory DD.OWN $0B 2 Owner’s user number DD.ATT $0D 1 Disk access attributes (D S PE PW PR E W R) DD.DSK $0F 2 Disk ID (a pseudo-random number used to detect diskette swaps) DD.FMT $10 1 Media format DD.SPT $11 2 Number of sectors per track. (Track 0 can use a different value specified by IT.T0S in the device descriptor.) DD.RES $13 2 Reserved for future use DD.SIZ $15 0 Minimum size of device descriptor V.TRAK $15 2 Number of the current track (the track that the head is on, and the track updated by the driver) V.BMB $17 1 "Bit-map use flag: 0 = Bit map is not in use (Disk driver routines must not alter V.BMB)" V.FILEHD $18 2 Open file list for this drive V.DISKID $1A 2 Disk ID V.BMAPSZ $1C 1 Size of bitmap V.MAPSCT $1D 1 Lowest reasonable bitmap sector V.RESBIT $1E 1 Reserved bitmap sector V.SCTKOF $1F 1 Sector/track byte V.SCOFST $20 1 Sector offset split from byte above V.TKOFST $22 4 Reserved for future use DRVMEM $26 . Size of each drive table

The format attributes (DD.FMT) are these:

Bit 0 Number of sides 0 = Single-sided 1 = Double-sided Bit 1 Density 0 = Single-density 1 = Double-density Bit 2 Track density 0 = Single (48 tracks per inch) 1 = Double (96 tracks per inch) RBF Device Driver Subroutines Like all device driver modules, RBF device drivers use a standard executable memory module format.

The execution offset address in the module header points to a branch table that has six 3-byte entries. Each entry is typically a long branch (LBRA) to the corresponding subroutine. The branch table is defined as follows:

ENTRY LBRA INIT Initialize drive LBRA READ Read sector LBRA WRITE Write sector LBRA GETSTA Get status LBRA SETSTA Set status LBRA TERM Terminate device

Ensure that each subroutine exits with the C bit of the condition code register cleared if no error occurred. If an error occurs, set the C bit and return an appropriate error code in Register B.

The rest of this chapter describes the RBF device driver subroutines and their entry and exit conditions. �Init Initializes a device and the device’s memory area.

Entry Conditions Y address of the device descriptor U address of the device memory area

Exit Conditions CC carry set on error B error code (if any)

Additional Information If you want NitrOS9 to verify disk writes, use the Request Memory system call (F$SRqMem) to allocate a 256-byte buffer area in which a sector can be read back and verified after a write. You must initialize the device memory area. For floppy diskette controllers, initialization typically consists of: Initializing V.NDRV to the number of drives with which the controller works Initializing DD.TOT (in the drive table) to a non-zero value so that Sector 0 can be read or written Initializing V.TRAK to $FF so that the first seek finds Track 0 Placing the IRQ service routing on the IRQ polling list, using the Set IRQ system call (F$IRQ) Initializing the device control registers (enabling interrupts if necessary) Prior to being called, the device memory area is cleared (set to zero), except for V.PAGE and V.PORT. (These areas contain the 24-bit device address.) Ensure the driver initializes each drive table appropriately for the type of diskette that the driver expects to be used on the corresponding drive. �Read Reads a 256-byte sector from a disk and places it in a 256-byte sector buffer.

Entry Conditions B MSB of the disk’s LSN X LSB of the disk’s LSN Y address of the path descriptor U address of the device memory area

Exit Conditions CC carry set on error B error code (if any)

Additional Information The following is a typical routine for using Read: Get the sector buffer address from PD.BUF in the path descriptor. Get the drive number from PD.DRV in the path descriptor. Compute the physical disk address from the logical sector number. Initiate the Read operation Copy V.BUSY to V.WAKE. The driver goes to sleep and waits for the I/O to complete. (The IRQ service routine is responsible for sending a wakeup signal.) After awakening, the driver tests V.WAKE to see if it is clear. If it is not clear, the driver goes back to sleep. Whenever you read LSN 0, you must copy the first part of this sector into the proper drive table. (Get the drive number from PD.DRV in the path descriptor.) The number of bytes to copy is in DD.SIZ. Use the drive number (PD.DRV) to compute the offset for the corresponding drive table as follows:

LDA PD.DRV,Y Get the drive number LDB #DRVMEM Get the size of a drive table MUL LEAX DRVBEG,U Get the address of the first table LEAX D,X Compute the address of the table �Write Writes a 256-byte sector buffer to a disk.

Entry Conditions B MSB of the disk LSN X LSB of the disk LSN Y address of the path descriptor U address of the device memory area

Exit Conditions CC carry set on error B error code (if any)

Additional Information Following is a typical routine for using Write: Get the sector buffer address from PD.BUF in the path descriptor. Get the drive number from PD.DRV in the path descriptor. Compute the physical disk address from the logical sector number. Initiate the Write operation. Copy V.BUSY to V.WAKE. The driver then goes to sleep and waits for the I/O to complete. (The IRQ service routine sends the wakeup signal.) After awakening, the driver tests V.WAKE to see if it is clear. If it is not, the driver goes back to sleep. If the disk controller cannot be interrupt-driven, it is necessary to perform a programmed I/O transfer. If PF.VFY in the path descriptor is equal to zero, read the sector back in and verify that it is written correctly. Verification usually does not involve a comparison of all of the data bytes. If disk writes are to be verified, the Init routine must request the buffer in which to place the sector when it is read back. Do not copy LSN 0 into the drive table when reading it back for verification. Use the drive number (PD.DRV) to compute the offset to the corresponding drive table as shown for the Read routine. �GetStats and SetStats Reads or changes device’s operating parameters.

Entry Conditions U address of the device memory area Y address of the path descriptor A status code

Exit Conditions CC carry set on error B error code (if any)

Additional Information Get/set the device’s operating parameters (status) as specified for the Get Status and Set Status system calls. GetStat and SetStat are wild card calls. It might be necessary to examine or change the register stack that contains the values of the 6809 registers at the time of the call. The address of the register stack is in PD.RGS, which is located in the path descriptor. You can use the following offsets to access any value in the register stack:

Reg. "Relative Address" Size "6809 Register" R$CC $00 1 Condition code register R$D $01 2 Register D R$A $01 1 Register A R$B $02 1 Register B R$DP $03 1 Register DP R$X $04 2 Register X R$Y $06 2 Register Y R$U $08 2 Register U R$PC $0A 2 Program counter

Register D overlays Registers A and B. �Term Terminate a device.

Entry Conditions U address of the device memory area

Exit Conditions CC carry set on error B error code (if any)

Additional Information This routine is called when a device is no longer in use in the system (when the link count of its device descriptor module becomes zero). Following is a typical routine for using Term: Wait until any pending I/O is completed. Disable the device interrupts. Remove the device from the IRQ polling list. If the Init routine reserved a 256-byte buffer for verifying disk writes, return the memory with the Return System Memory system call (F$SRtMem). �IRQ Service Routine Services device interrupts

Additional Information The IRQ Service routine sends a wakeup signal to the process indicated by the process ID in V.WAKE when the I/O is complete. It then clears V.WAKE as a flag to indicate to the main program that the IRQ has indeed occurred. When the IRQ Service routine finishes servicing an interrupt, it must clear the carry and exit with an RTS instruction. Although this routine is not included in the device driver module branch table and is not called directly by the RBF manager, it is a key routine in interrupt-driven drivers. Its function is to: Service the device interrupts (receive data from device or send data to it). The IRQ Service routine puts its data into and gets its data from buffers that are defined in the device memory area. Wake up a process that is waiting for I/O to be completed. To do this, the routine checks to see if there is a process ID in V.WAKE (if the bit is non-zero); if so, it sends a wakeup signal to that process. If the device is ready to send more data, and the out buffer is empty, disable the device’s ready to transmit interrupts. �Boot (Bootstrap Module) Loads the boot file into RAM.

Entry Conditions None

Exit Conditions D size of the boot file (in bytes) X address at which the boot file was loaded into memory CC carry set on error B error code (if any)

Additional Information The Boot module is not part of the disk driver. It is a separate module that is stored on the boot track of the system disk with Krn and REL. The bootstrap module contains one subroutine that loads the bootstrap file and related information into memory. It uses the standard executable module format with a module type of $C. The execution offset in the module header contains the offset to the entry point of this subroutine. The module gets the starting sector number and size of the OS9Boot file from LSN 0. NitrOS9 allocates a memory area large enough for the Boot file. Then, it loads the Boot file into this memory area. Following is a typical routine for using Boot: Read LSN 0 from the disk into a buffer area. The Boot module must pick its own buffer area. LSN 0 contains the values for DD.BT (the 24-bit LSN of the bootstrap file), and DD.BSZ (the size of the bootstrap file in bytes). Get the 24-bit LSN of the bootstrap file from DD.BT. Get the size of the bootstrap file from DD.BSZ. The Boot module is contained in one logically contiguous block beginning at the logical sector specified in DD.BT and extending for DD.BSZ/256+1 sectors. Use the NitrOS9 Request System Memory system call (F$SRqMem) to request the memory area in which the Boot file is loaded. Read the Boot file into this memory area. Return the size of the Boot file and its location. Boot file is loaded.

Sequential Character File Manager The Sequential Character File Manager (SCFMAN) supports devices that operate on a character-by-character basis. These include terminals, printers, and modems.

SCF is a re-entrant subroutine package. The I/O manager calls the SCF manager for I/O system handling of sequential, character-oriented devices. The SCF manager includes the extensive I/O editing functions typical of line-oriented operations, such as:

backspace line delete line repeat auto line feed screen pause return delay padding

The SCF-type device driver modules are VTIO, SCBBT, and SCBBP. They run the video display, printer, and serial ports respectively. See the NitrOS9 Commands manual for additional Color Computer I/O devices. SCF Line Editing Functions The SCF manager supports two sets of read and write functions. I$Read and I$Write pass data with no modification. I$ReadLn and I$WritLn provide full line editing of device functions. Read and Write The Read and Write system calls to SCF-type devices correspond to the BASIC09 GET and PUT statements. While they perform little modification to the data they pass, they do filter out keyboard interrupt, keyboard terminate, and pause characters. (Editing is disabled if the corresponding character in the path descriptor contains a zero.

Carriage returns are not followed by line feeds or nulls automatically, and the high order bits are passed as sent/received. Read Line and Write Line The Read Line and Write Line system calls to SCF-type devices correspond to the BASIC09 INPUT, PRINT, READ, and WRITE statements. They provide full line editing of all functions enabled for a particular device.

The system initializes I$ReadLn and I$WritLn functions when you first use a particular device. (NitrOS9 copies the option table from the device descriptor table associated with the specific device.

Later, you can alter the calls—either from assembly-language programs (using the Get Status system call), or from the keyboard (using the TMODE command). All bytes transferred by I$ReadLn and I$WritLn have the high order bit cleared. SCF Definitions of the Path Descriptor The PD.FST and PD.OPT sections of the path descriptor are reserved for an used by the SCF file manager.

The following table describes the SCF manager’s use of PD.FST and PD.OPT. For your convenience, the table also includes the other sections of the path descriptor.

The PD.OPT section contains the values that determine the line editing functions. It contains many device operating parameters that can be read or written by the Set Status or Get Status system call. Any values not set by this table default to zero.

Note: You can disable most of the editing functions by setting the corresponding control character in the path descriptor to zero. You can use the Set Status system call or the TMODE command to do this. Or, you can go a step further by setting the corresponding control character value in the device descriptor module to zero.

To determine the default settings for a specific device, you can inspect the device descriptor.


Name "Relative Address" "Size (Bytes)" Use Universal Section (Sale for all file managers) PD.PD $00 1 Path number PD.MOD $01 1 "Access mode: 1 = read 2 = write 3 = update" PD.CNT $02 1 Number of open images (paths using this path descriptor) PD.DEV $03 2 Address of the associated device table entry PD.CPR $05 1 Current process ID PD.RGS $06 2 Address of the caller’s 6809 register stack PD.BUF $08 2 Address of the 256-byte data buffer (if used)

Name "Relative Address" "Size (Bytes)" Use SCF Path Descriptor Definitions (PD.FST Section) PD.DV2 $0A 2 Device table address of the second (echo) device PD.RAW $0C 1 "Edit flag: 0 = raw mode 1 = edit mode" PD.MAX $0D 2 Read Line maximum character count PD.MIN $0F 1 Devices are mine if cleared PD.STS $10 2 Status routine module address PD.STM $12 2 Reserved for status routine

Name "Relative Address" "Size (Bytes)" Use "SCF Option Section Definition (PD.OPT Section) (Copied from the device descriptor)" PD.DTP $20 1 "Device class: 0 = SCF 1 = RBF 2 = PIPE 3 = SBF" PD.UPC $21 1 "Case: 0 = uppercase and lowercase 1 = uppercase only" PD.BSO $22 1 "Backspace: 0 = backspace 1 = backspace, space, and backspace" PD.DLO $23 1 "Delete: 0 = backspace over line 1 = carriage return, line feed" PD.EKO $24 1 "Echo: 0 = no echo 1 = echo" PD.ALF $25 1 "Auto line feed: 0 = no auto line feed 1 = auto line feed" PD.NUL $26 1 "End-of-line null count: N = number of nulls ($00) sent after each carriage return or carriage return and line feed (n = $00-$FF)" PD.PAU $27 1 "End of page pause: 0 = no pause 1 = pause" PD.PAG $28 1 Number of lines per page PD.BSP $29 1 Backspace character PD.DEL $2A 1 Delete-line character PD.EOR $2B 1 "End-of-record character (End-of-line character) Read only. Normally set to $0D 0 = Terminate read-line only at the end of the file" PD.EOF $2C 1 End-of-file character (read only) PD.RPR $2D 1 Reprint-line character PD.DUP $2E 1 Duplicate-last-line character PD.PSC $2F 1 Pause character PD.INT $30 1 Keyboard-interrupt character PD.QUT $31 1 Keyboard-terminate character PD.BSE $32 1 Backspace-echo character PD.OVF $33 1 Line-overflow character (bell CTRL-G) PD.PAR $34 1 Device initialization value (parity) PD.BAU $35 1 Software settable baud rate PD.D2P $36 2 Offset to second device name string PD.XON $38 1 ACIA XON character PD.XOFF $39 1 ACIA XOFF character PD.ERR $3A 1 Most recent I/O error status PD.TBL $3B 2 Copy of device table address PD.PLP $3D 2 Path descriptor list pointer PD.PST $3F 1 Current path status

PD.EOF specifies the end-of-file character. If this is the first and only character that is input to the SCF device, SCF returns an end-of-file error on Read or ReadLn.

PD.PSC specifies the pause character, which suspends output to the device before the next end-of-record character. The pause character also deletes any type-ahead input for ReadLn.

PD.INT specifies the keyboard-interrupt character. When the character is received, the system sends a keyboard-terminate signal to the last user of a path. The character also terminates the current I/O request (if any) with an error identical to the keyboard interrupt signal code.

PD.QUT specifies the keyboard-terminate character. When this character is received, the system sends a keyboard-terminate signal to the last user of a path. The system also cancels the current I/O request (if any) by sending an error code identical to the keyboard interrupt signal code.

PD.PAR specifies the parity information for external serial devices.

PD.BAU specifies baud rate, word length, and stop bit information for serial devices.

PD.XON contains either the character used to enable transmission of characters or a null character that disables the use of XON.

PD.XOFF contains either the character used to disable transmission of characters or a null character that disables the use of XOFF. SCF-Type Device Descriptor Modules The following chart shows how the initialization table in the device descriptors is used for SCF-type devices. The values are those the I/O manager copies from the device descriptor to the path descriptor.

An SCF editing function is turned off if its corresponding value is set to zero. For example, if IT.EOF is set to zero, there is no end-of-file character.


Name "Relative Address" "Size (Bytes)" Use (header) $00-$11 Standard device descriptor module header IT.DVC $12 1 "Device class: 0 = SCF 1 = RBF 2 = PIPE 3 = SBF" IT.UPC $13 1 "Case: 0 = upper- and lowercase 1 = uppercase only" IT.BSO $14 1 "Backspace: 0 = backspace 1 = backspace, space, and backspace" IT.DLO $15 1 "Delete: 0 = backspace over line 1 = carriage return" IT.EKO $16 1 "Echo: 0 = echo off 1 = echo on" IT.ALF $17 1 "Auto line feed: 0 = auto line feed disabled 1 = auto line feed enabled" IT.NUL $18 1 End-of-line null count IT.PAU $19 1 "Pause: 0 = end-of-page pause disabled 1 = end-of-page pause enabled" IT.PAG $1A 1 Number of lines per page IT.BSP $1B 1 Backspace character IT.DEL $1C 1 Delete-line character IT.EOR $1D 1 End-of-record character IT.EOF $1E 1 End-of-file character IT.RPR $1F 1 Reprint-line character IT.DUP $20 1 Duplicate-last-line character IT.PSC $21 1 Pause character IT.INT $22 1 Interrupt character IT.QUT $23 1 Quit character IT.BSE $24 1 Backspace echo character IT.OVF $25 1 Line-overflow character (bell) IT.PAR $26 1 Initialization value—used to initialize a device control register when a path is opened to it (parity) IT.BAU $27 1 Baud rate IT.D2P $28 2 Attached device name string offset IT.XON $2A 1 X-ON character IT.XOFF $2B 1 X-OFF character IT.COL $2C 1 Number of columns for display IT.ROW $2D 1 Number of rows for display IT.WND $2E 1 Window number IT.VAL $2F 1 Data in rest of descriptor is valid IT.STY $30 1 Window type IT.CPX $31 1 X cursor position IT.CPY $32 1 Y cursor position IT.FGC $33 1 Foreground color IT.BGC $34 1 Background color IT.BDC $35 1 Border color SCF-Type Device Driver Modules An SCF-type device driver module contains a package of subroutines that perform raw (unformatted) data I/O transfers to or from a specific hardware controller. Such a module is usually re-entrant so that one copy of the module can simultaneously run several devices that use identical I/O controllers. The I/O manager allocates a permanent memory area for each controller sharing the driver.

The size of the memory area is defined in the device driver module header. The I/O manager and SCF use some of the memory area. The device driver can use the rest in any way (typically as variables and buffers). Typically, the driver uses the area as follows:

Name "Relative Address" "Size (Bytes)" Use V.PAGE $00 1 Port extended 24-bit address V.PORT $01 2 Device base address (defined by the I/O manager) V.LPRC $03 1 ID of the last active process V.BUSY $04 1 "ID of the active process (defined by RBF): 0 = no active process" V.WAKE $05 1 "ID of the process to reawaken after the device completes I/O (defined by the device driver): 0 = no waiting process" V.USER $06 0 Beginning of file manager specific storage V.TYPE $06 1 Device type or parity V.LINE $07 1 Lines left until the end of the page V.PAUS $08 1 "Pause request: 0 = no pause requested" V.DEV2 $09 2 Attached device memory area V.INTR $0B 1 Interrupt character V.QUIT $0C 1 Quit character V.PCHR $0D 1 Pause character V.ERR $0E 1 Error accumulator V.XON $0F 1 XON character V.XOFF $10 1 XOFF character V.KANJI $11 1 Reserved V.KBUF $12 2 Reserved V.MODADR $14 2 Reserved V.PDLHD $16 2 Path descriptor list header V.RSV $18 5 Reserved V.SCF $1D 0 End of SCF memory requirements FREE $1D 0 Free for the device driver to use

V.LPRC contains the process ID of the last process to use the device. The IRQ service routing sends this process the proper signal if it receives a quit character or an interrupt character. V.LPRC is defined by SCF.

V.BUSY contains the process ID of the process that is using the device. (If the device is not being used, V.BUSY contains a zero.) The process ID is used by SCF to prevent more than one process from using the device at the same time. V.BUSY is defined by SCF. SCF Device Driver Subroutines Like all device drivers, SCF device drivers use a standard executable memory module format.

The execution offset address in the module header points to a branch table that has six 3-byte entries. Each entry is typically an LBRA to the corresponding subroutine. The branch table is defined as follows:

ENTRY LBRA INIT Initialize driver LBRA READ Read character LBRA WRITE Write character LBRA GETSTA Get status LBRA SETSTA Set status LBRA TERM Terminate device

If no error occurs, each subroutine exits with the C bit in the Condition Code register cleared. If an error occurs, each subroutine sets the C bit and returns an appropriate error code in Register B.

The rest of this chapter describes these subroutines and their entry and exit conditions. �Init Initializes device control registers and enables interrupts if necessary.

Entry Conditions Y address of the device descriptor U address of the device memory area

Exit Conditions CC carry set on error B error code (if any)

Additional Information Prior to being called, the device memory area is cleared (set to zero), except for V.PAGE and V.PORT. (V.PAGE and V.PORT contain the device address.) There is no need to initialize the part of the memory area used by the I/O manager and SCF. Follow these steps to use Init: Initialize the device memory area. Place the IRQ service routine on the IRQ polling list, use the Set IRQ system call (F$IRQ). Initialize the device control registers. �Read Reads the next character from the input buffer.

Entry Conditions Y address of the path descriptor U address of the device memory area

Exit Conditions A character read CC carry set on error B error code (if any)

Additional Information This is a step by step description of a Read operation: Read gets the next character from the input buffer. If no data is ready, Read copies its process ID from V.BUSY into V.WAKE. It then uses the Sleep system call to put itself to sleep. Later, when Read receives data, the IRQ service routine leaves the data in a buffer. Then, the routine checks V.WAKE to see if any process is waiting for the device to complete I/O. If so, the IRQ service routine sends a wakeup signal to the waiting process. Data buffers are not automatically allocated. If a buffer is used, it defines it in the device memory area. �Write Sends a character (places a data byte in an output buffer) and enables the device output interrupts.

Entry Conditions A character to write Y address of the path descriptor U address of the device memory area

Exit Conditions CC carry set on error B error code (if any)

Additional Information If the data buffer is full, Write copies its process ID from V.BUSY into V.WAKE. Write then puts itself to sleep. Later, when the IRQ service routine transmits a character and makes room for more data, it checks V.WAKE to see if there is a process waiting for the device to complete I/O. If there is, the routine sends a wakeup signal to that process. Write must ensure that the IRQ service routine that starts it begins to place data in the buffer. After an interrupt is generated, the IRQ service routine continues to transmit data until the data buffer is empty. Then, it disables the device’s ready-to-transmit interrupts. Data buffers are not allocated automatically. If a buffer is used, define it in the device memory area. �GetSta and SetSta Gets/sets device operating parameters (status) as specified for the Get Status and Set Status system calls. GetSta and SetSta are wildcard calls.

Entry Conditions A depends on the function code Y address of the path descriptor U address of the device memory area Other registers depend on the function code.

Exit Conditions CC carry set on error B error code (if any) Other registers depend on the function code

Additional Information Any codes not defined by the I/O manager or SCF are passed to the device driver. You might need to examine or change the register stack that contains the values of the 6809 registers at the time of the call. The address of the register stack can be found in PD.RGS, which is located in the path descriptor. You can use the following offsets to access any value in the register packet:

Reg. "Relative Address" Size "6809 Register" R$CC $00 1 Condition code register R$D $01 2 Register D R$A $01 1 Register A R$B $02 1 Register B R$DP $03 1 Register DP R$X $04 2 Register X R$Y $06 2 Register Y R$U $08 2 Register U R$PC $0A 2 Program counter The function code is retrieved from R$B on the user stack.

�Term Terminates a device. Term is called when a device is no longer in use (when the link count of the device descriptor module becomes zero).

Entry Conditions U pointer to the device memory area

Exit Conditions CC carry set on error B error code (if any)

Additional Information To use Term: Wait until the IRQ service routine empties the output buffer. Disable the device interrupts. Remove the device from the IRQ polling list. When Term closes the last path to a device, NitrOS9 returns to the memory pool the memory that the device used. If the device has been attached to the system using the I$Attach system call, NitrOS9 does not return the static storage for the driver until an I$Detach call is made to the device. Modules contained in the Boot file are never terminated, even if their link counts reach zero. �IRQ Service Routine Receives device interrupts. When I/O is complete, the routine sends a wakeup signal to the process identified by the process ID in V.WAKE. The routine also clears V.WAKE as a flag to indicate to the main program that the IRQ has occurred.

Additional Information The IRQ Service Routine is not included in the device driver branch tables, and is not called directly by SCF. However, it is a key routine in device drivers. When the IRQ Service routine finishes servicing an interrupt, the routine must clear the carry and exit with an RTS instructions. Here is a typical sequence of events that the IRQ Service Routing performs: Service the device interrupts (receive data from the device or send data to it). Ensure this routine puts its data into and gets its data from buffers that are defined in the device memory area. Wake up any process that is waiting for I/O to complete. To do this, the routine checks to see if there is a process ID in V.WAKE (a value other than zero); if so, it sends a wakeup signal to that process. If the device is ready to send more data, and the output buffer is empty, disable the device’s ready-to-transmit interrupts. If a pause character is received, set V.PAUS in the attached device storage area to a value other than zero. The address of the attached device memory area is in V.DEV2. If a keyboard terminate or interrupt character is received, signal the process in V.LPRC (last known process) if any.

The Pipe File Manager (PIPEMAN) The Pipe file manager handles control or processes that use paths to pipes. Pipes allow concurrently executing processes to send each other data by using the output of one process (the writer) as input to a second process (the reader). The reader gets input from the standard input. The exclamation point (!) operator specifies that the input or output is from or to a pipe. The Pipe file manager allocates a 256-byte block and a path descriptor for data transfer. The Pipe file manager also determines which process has control of the pipe. The Pipe file manager has the standard file manager branch table at its entry point:

ENTRY LBRA Create LBRA Open LBRA MakDir LBRA ChgDir LBRA Delete LBRA Seek LBRA PRead LBRA PWrite LBRA PRdLn LBRA PWrLn LBRA GetStat LBRA SetStat LBRA Close

You cannot use MakDir, ChgDir, Delete, and Seek with pipes. If you try to do so, the system returns E$UNKSVC (unknown service request). GetStat and SetStat are also no-action service routines. They return without error.

Create and Open perform the same functions. They set up the 256-byte data exchange buffer and save several addresses in the path descriptor.

The Close request checks to see if any process is reading or writing through the pipe. If not, NitrOS9 returns the buffer.

PRead, PWrite, PRdLn, and PWrLn read data from the buffer and write data to it.

The ! operator tells the Shell that processes wish to communicate through a pipe. For example:

poc1 ! proc2

In this example, shell forks Proc1 with the standard output path to a pipe and forks Proc2 with the standard input path from a pipe.

Shell can also handle a series of processes using pipe. For example:

proc1 ! proc2 ! proc3 ! proc4

The following outline shows how to set up pipes between processes:

Open /pipe save path in variable x Dup path #1 save stdout in variable y Close #1 make path available Dup x put pipe in stdout (Dup uses lowest available) Fork proc1 fork process 1 Close #1 make path available Dup y restore stdout Close y make path available

Dup path #0 save stdin in Y Close #0 make path available Dup x put pipe in stdin Fork proc2 fork process 2 Close #0 make path available Dup y restore stdin Close x no longer needed Close y no longer needed

Example: The following example shows how an application can initiate another process with the stdin and stdout routed through a pipe:

Open /pipe1 save path in variable a Open /pipe2 save path in variable b Dup 0 save stdin in variable x Dup 1 save stdout in variable y Close #0 make stdin path available Close #1 make stdout path available Dup a make pipe1 stdin Dup b make pipe2 stdout Fork new process Close #0 make stdin path available Close #1 make stdout path available Dup x restore stdin Dup y restore stdout Return a&b return pipe path numbers to caller

System Calls