INITIALIZATION Knowledgebase: ki-dev-large Base Query: Please summarize the whole context. It is important that you include a summary for each file. All files should be included, so please make sure to go through the entire context Model: gemini-1.5-pro-preview-0409 Use Curl?: ================================================== **Elapsed Time: 0.00 seconds** ================================================== ROUTING Query type: summary ================================================== **Elapsed Time: 2.32 seconds** ================================================== PRIMER Primer: You are Simon, a highly intelligent personal assistant in a system called KIOS. You are a chatbot that can read knowledgebases through the "CONTEXT" that is included in the user's chat message. Your role is to act as an expert at summarization and analysis. In your responses to enterprise users, prioritize clarity, trustworthiness, and appropriate formality. Be honest by admitting when a topic falls outside your scope of knowledge, and suggest alternative avenues for obtaining information when necessary. Make effective use of chat history to avoid redundancy and enhance response relevance, continuously adapting to integrate all necessary details in your interactions. Use as much tokens as possible to provide a detailed response. ================================================== **Elapsed Time: 0.40 seconds** ================================================== FINAL QUERY Final Query: CONTEXT: ########## File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20%20UsingOberon.pdf Page: 1 Context: # How to use the Oberon System **Niklaus Wirth, November 2015** The Oberon System was designed and implemented in 1990 as an example of a modern, interactive operating system with windows and mouse. In contrast to commercial systems, it was to be simple and well-structured in order to be published in its entirety and available for teaching the essentials. It was programmed in the concise language Oberon, and it requires only a small fraction of the resources which commercial systems demand. ## How to begin After start-up (power on) the following layout appears on the display: | space for viewers to be opened | Oberon V5 | |------------------------------|------------------| | | (log) | | | System.Open ^ | | | Edit.Open ^ | | | (Standard tool) | On the right side, there are two windows (called *viewers*) vertically arranged. The top viewer shows a log, in which the system records the user's actions. (It displays the text *Oberon.Log*). The lower viewer is the standard command tool, containing various general commands. (We call texts containing commands *tools*). Commands in Oberon have the form *M.P*, where *M* is the name of the module containing the procedure *P*. Commands may occur in any text, and they are activated by pointing at them and clicking the middle mouse button (the *command button*). On the left side appears an empty space where new viewers may be placed. If, for example, we wish to display a text (file) named *Sample.Mod*, we select the name by pointing at it and clicking the right mouse button (the *select button*) and then activate the command *Edit.Open* in the standard tool viewer. If the name does not appear somewhere on the screen already, type the name (see editing texts below) to enter it. Now a new viewer containing the text is opened in the area to the left. If a text (file) with the specified name does not exist, an empty viewer appears. The viewers now visible are *text viewers*. They consist of two parts, called *frames*. The narrow one on top is called the *title bar*. It contains the name of the displayed text and several commands for viewer handling and text editing, among them. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20%20UsingOberon.pdf Page: 2 Context: # System Commands | Command | Description | |----------------|--------------------------------------| | System.Close | close the viewer | | Edit.Store | store the text as a file | | Edit.Search | search for the selected word from the position of the caret | ## Editing Texts The most frequent task is editing a text. For this case, some commands can be issued by simple mouse clicks alone rather than clicking the command name. When a text is to be entered, it is always inserted at the position of the caret displayed as a hook. The caret is positioned by pointing (with the cursor) at the desired position and clicking the left button (pointer button). Then the text is entered by simply typing it. A piece of text can be selected in order to be subjected to a subsequent command. This is done by moving the cursor over the piece of text while holding the right button (select button) pressed. Often it is only necessary to select the first character of a parameter. Selection can be turned into deletion by briefly clicking also the left button while holding the right button pressed. This is called inter-clicking. Similarly, a piece of text being selected can be copied over to the caret position by inter-clicking the middle button. The functions of the mouse buttons are summarized by the following table: | Inter-click | left | middle | right | |----------------|---------------------|---------------|------------------| | Primary click: | set caret | - | copy | | | command | - | - | | | select | delete | copy | The keyboard features two special keys, so-called control keys. The `Esc` key undoes selections. The `Backspace` key deletes the character left of the caret. Text viewers have a scroll bar at their left edge. While the cursor is in the scroll bar, it assumes the form of an up/down pointer to show its difference in functionality. Clicking a button then causes the text to scroll up or down. - **Left button:** the current line moves up to the top of the viewer - **Middle button:** shows the section at the relative position given by the cursor - **Right button:** the top line moves down to the current cursor position ## Viewer Handling Viewers can be extended, moved, or copied. A viewer is enlarged or shrunk by clicking the left button, while the cursor is in the title bar, and then dragging the bar up or down. A viewer is moved to another location by also inter-clicking with the middle button. A duplicate of a viewer may be generated by activating the command `System.Copy` in the title bar. Note that in this case the old and the new viewer show the same text, and not a copy of the text. This facility comes in handy when a piece of text needs to be #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20%20UsingOberon.pdf Page: 3 Context: # System Tool Commands The command `System.grow` in the title bar generates a copy extending over the entire column (or over the entire display). By closing the viewer (command `System.close` in the title bar), the original viewer reappears. We may imagine that `grow` lifts a viewer to an overlay in the third dimension. ## Commands The following commands appear in the standard tool `System.Tool`. The character `^` indicates that a name must be selected previously. The command then applies to that named text (file, as in the example above). The character `~` indicates that a list of names must be inserted before the `~` character, and the command will apply to all named objects. | Command | Description | |-------------------------|---------------------------------------------------------| | `System.Open ^` | open viewer in the system track to the right | | `System.Recall` | close the last viewer opened | | `Edit.Open ^` | open viewer in the user track to the left | | `Edit.Recall` | undo last editing operation | | `Edit.ChangeFont` | applies to selected piece of text | | `Edit.SetFont` | use specified font for subsequent input | | `System.Directory ^` | search directory for specified file names | | `System.Free ~` | remove specified modules from store | | `System.CopyFiles ->` | e.g. `file1 => file2 file3 => file4` | | `System.RenameFiles ->` | e.g. `file1 => file2 file3 => file4` | | `System.DeleteFiles ->` | e.g. `file1 file2 file3 ->` (from file directory) | | `System.ShowModules` | | | `System.ShowCommands ^`| compile selected text | | `Hilbert.Draw` | draw Hilbert curve, as an example | When clicking a command `M.P.`, module `M` is searched in the file store and, if found and not already present, loaded into main store. Then its command `P` is searched and executed (if found). A list of loaded modules can be generated by the command `System.ShowModules`, and a list of its commands is obtained by the command `System.ShowCommands`. Any parameter-less procedure in any (compiled) module is accessible as a command. Its parameters are accessed via a scanner. As an example, consider the following short program: ```pascal MODULE M0: IMPORT Texts, Oberon; VAR W: Texts.Writer; PROCEDURE P0; VAR sum: INTEGER; S: Texts.Scanner; BEGIN sum := 0; Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S); WHILE S.Class = Texts.Int DO Texts.Writeln(W, S.i, ':'); sum := sum + S.i; Texts.Scan(S); END; END; ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20%20UsingOberon.pdf Page: 4 Context: ``` Texts.Write(W, sum, 8); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END P0; BEGIN Texts.OpenWriter(W) END M0. After its successful compilation (ORP.Compile @), the command M0.PO 2 3 5 7 11 causes these numbers and their sum to be output to the standard viewer **System.Log**. ## The core of the Oberon System The system's core consists of a loop which continuously senses for a command to appear. The command is identified, control is dispatched, and the command is executed. A command may stem from an explicit click (middle button) on a text of the form M.P, or it may be a click of the left or right mouse buttons (see editing commands). A further source of input is the keyboard. If any key is pressed, this is interpreted as a command to read that character. Exceptions are the **esc**, **ctrl-z** (or **F1**), and **backspace** keys. **esc** is interpreted as a command to undo all selections, **backspace** to remove the character left of the caret and **ctrl-z** to set the global marker. ``` yes no mouse key? | keyboard? ``` The initially loaded system contains, apart from module Oberon, the command module **System**, a text system (modules TextFrames, MenuViewers, Texts, Fonts, Input), a viewer system (modules Viewers, Display), the loader and linker (module Modules), a file system (modules Files, FileDir), and the disk space manager and the garbage collector (module Kernel). The compiler is loaded on demand, like other application programs. [Project Oberon - ETH Zurich](https://www.inf.ethz.ch/personal/wirth/ProjectOberon/index.html) [Project Oberon](http://www.projectoberon.com/) ``` #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 1 Context: # POST Memory Manager Specification **Version 1.01** **November 21, 1997** This specification has been made available to the public. You are hereby granted the right to use, implement, reproduce, and distribute this specification with the foregoing rights at no charge. This specification is, and shall remain, the property of Phoenix Technologies Ltd. (“Phoenix”), and Intel Corporation (“Intel”). --- NEITHER PHOENIX NOR INTEL MAKE ANY REPRESENTATION OR WARRANTY REGARDING THIS SPECIFICATION OR ANY PRODUCT OR ITEM DEVELOPED BASED ON THIS SPECIFICATION. USE OF THIS SPECIFICATION FOR ANY PURPOSE IS AT THE RISK OF THE PERSON OR ENTITY USING IT. PHOENIX AND INTEL DISCLAIM ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND FREEDOM FROM INFRINGEMENT. WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, NEITHER PHOENIX NOR INTEL MAKE ANY WARRANTY OF ANY KIND THAT ANY ITEM DEVELOPED BASED ON THIS SPECIFICATION, OR ANY PORTION OF IT, WILL NOT INFRINGE ANY COPYRIGHT, PATENT, TRADE SECRET OR OTHER INTELLECTUAL PROPERTY RIGHT OF ANY PERSON OR ENTITY IN ANY COUNTRY. #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 2 Context: # TABLE OF CONTENTS 1. **INTRODUCTION**.............................................................................................3 1.1 **REVISION HISTORY**..................................................................................3 1.1.1 **Technical Editors**...............................................................................3 1.2 **RELATED DOCUMENTS**............................................................................3 1.3 **TERMS**....................................................................................................4 2. **FUNCTIONALITY**............................................................................................5 2.1 **OVERVIEW**..............................................................................................5 2.1.1 **Why a PMM?**...................................................................................5 2.1.2 **PMM Model**....................................................................................5 2.2 **CLIENTS**..................................................................................................5 3. **PMM SERVICES INTERFACE**.........................................................................6 3.1 **DETECTING PMM SERVICES**...................................................................6 3.1.1 **PMM Structure**................................................................................6 3.1.2 **Detection Algorithm**.....................................................................6 3.2 **PMM SERVICES**.....................................................................................6 3.2.1 **Interface Style**..............................................................................7 3.2.2 **Stack Requirements**.....................................................................7 3.2.3 **Memory Block Alignment**................................................................7 3.2.4 **Accessing Extended Memory**........................................................7 3.3 **PMMALLOCATE - FUNCTION 0**...............................................................7 3.3.1 **Description**....................................................................................7 3.3.2 **Function Prototype**........................................................................8 3.3.3 **Parameters**....................................................................................8 3.4 **PMMFREE - FUNCTION 1**......................................................................9 3.4.1 **Description**....................................................................................9 3.4.2 **Function Prototype**........................................................................9 3.4.3 **Parameters**....................................................................................9 3.5 **PMMDEALLOCATE - FUNCTION 2**..........................................................9 3.5.1 **Description**....................................................................................9 3.5.2 **Function Prototype**.......................................................................10 3.5.3 **Parameters**...................................................................................10 3.6 **C LANGUAGE CODING EXAMPLE**..........................................................11 3.7 **ASSEMBLY LANGUAGE CODING EXAMPLE**........................................11 4. **CONVENTIONS FOR CREATING MEMORY BLOCK HANDLES**......................14 4.1 **NAME SELECTION**.................................................................................14 4.1.1 **ISA Product Identifiers**.................................................................14 4.1.2 **Convention for Selecting Handle Values**....................................14 4.2 **RECOMMENDED METHOD FOR THE USE OF NAMED BLOCKS**...........15 4.3 **FINDING OTHER CARDS IN THE SYSTEM**...........................................16 #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 3 Context: # 1 Introduction ## 1.1 Revision History | Revision | Date | Changes | |----------|--------------------|---------------------------------------------------------------------------------------------| | 1.01 | November 21, 1997 | Included guidelines on using extended memory during POST. | | | | Clarified the processor mode and the state of Gate A20 during POST. | | | | Definitions for the terms: Big Real Mode, Extended Memory, and Gate A20 were added. | | | | Changed to not clear the contents of memory blocks when they are deallocated. | | | | Simplified the assembly language coding example by using 32-bit instructions and operands. | | | | Clarified the 'C' language code example by adding a function to find the PMM structure. | | 1.0 | September 20, 1996 | Approved for public release. | ## 1.1.1 Technical Editors **Scott Townsend** Phoenix Technologies Ltd. 135 Technology Drive Irvine, CA 92618 Phone: (714) 790-2125 Fax: (714) 790-2001 Email: [Scott.Townsend@Phoenix.com](mailto:Scott.Townsend@Phoenix.com) **Bob Hale** Intel Corporation 5200 N.E. Elam Young Parkway Hillsboro, OR 97124-6497 Phone: (503) 696-4249 Fax: (503) 648-6705 Email: [robert_p_hale@ccm2.hf.intel.com](mailto:robert_p_hale@ccm2.hf.intel.com) ## 1.2 Related Documents | Title | Author | Version | |------------------------------------|------------------------------|---------| | BIOS Boot Specification | Phoenix, Intel, Compaq | 1.01 | | Plug and Play BIOS Specification | Compaq, Phoenix, Intel | 1.0A | | EISA Specification | BCPR Services, Inc. | 3.12 | #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 4 Context: # 1.3 Terms ## Big Real Mode **Big Real Mode** is a modified version of the processor’s real mode with the segment limits changed from 1MB to 4GB. Big real mode allows the BIOS or an Option ROM to read and write extended memory without the overhead of protected mode. The BIOS puts the processor in big real mode during POST to allow simplified access to extended memory. The processor will be in big real mode while the PMM services are callable. ## BIOS The **Basic Input Output System** is the system software embedded on a chip located on the computer’s main board. The BIOS executes POST to test and initialize the system components and then boots the operating system. The BIOS also handles the low-level input/output to the various peripheral devices connected to the computer at runtime. Additionally, most BIOSes have a Setup program that allows the user to configure the system. ## Extended Memory The **Extended Memory** area starts at memory address 1MB and ends at memory address 4GB. Extended memory is normally only accessible when the processor is in protected mode. One exception to this is big real mode (see above). Section 3.2.4 provides guidelines as to how a PMM client may access extended memory. ## Gate A20 **Gate A20** controls 1MB memory wrap-around. When Gate A20 is enabled, it forces memory accesses to wrap around and fall within the 0-1MB area by forcing address line 20 to be zero. This has the effect of not allowing access to extended memory. When Gate A20 is disabled, memory accesses beyond 1MB do not wrap around, thus allowing access to all of extended memory. ## Option ROM An **Option ROM** (Read Only Memory) is a software component located in a ROM chip on an add-in card or the system board. Its physical address is in system memory between addresses C0000H and DFFFFH. The BIOS may copy the component to shadow memory during POST. An Option ROM is characterized by the first two locations containing a two-byte signature of 55h AAh. Option ROMs are responsible for initializing their associated hardware, allowing it to be available to the rest of the system for booting or runtime. ## Paragraph A **Paragraph** is 16 contiguous bytes of memory. Paragraph alignment of data means that the address of the data is of the form xxxx0h. ## PMM The **POST Memory Manager** is a software component of the BIOS that provides for the allocation of memory blocks during system POST. ## POST The **Power-On Self-Test** is the part of the BIOS that takes control immediately after the computer is turned on. POST initializes the computer hardware in preparation for loading the operating system. ## Run-Time Execution of run-time software takes place after the operating system has loaded. BIOS run-time services are available at POST and remain callable after the operating system has booted. Application programs and operating systems call BIOS run-time services for hardware-related functions. The PMM services are not callable during run-time, and buffers allocated by the PMM during POST are not available at runtime. #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 5 Context: ``` # 2 Functionality ## 2.1 Overview This specification describes the functionality and interface of the POST Memory Manager (PMM). The PMM provides memory allocation only during POST. Memory blocks allocated during POST will not be available during run time because it is assumed that the operating system gains ownership of all memory when it loads. ### 2.1.1 Why a PMM? The need for a PMM arises out of the increased usage of memory by internal BIOS modules, as well as the burgeoning size of Option ROMs. As the system BIOS has become more complex, it has been split up into separate components, each with their own requirements for execution. Since PCI Option ROMs are shadowed and have a mechanism for reducing their final run-time footprint, they often utilize a memory block for storage of initialization data. In addition, many newer PCI Option ROMs are now using compression and need a memory block to decompress their code into before finalizing their run-time image. The capability to organize the usage of memory during POST is necessary in order to keep various software components from over-writing each other’s data areas. Option ROMs can utilize the PMM to allocate memory blocks for usage during initialization without danger of corrupting the data area of a system BIOS component or another Option ROM. ### 2.1.2 PMM Model The PMM utilizes a client/server model, in which the server is the PMM Services and the clients are internal BIOS code and Option ROMs. Clients allocate memory blocks from the PMM utilizing the PMM Services. The PMM allows clients to allocate either conventional or extended memory blocks to aid in their initialization. A buffer may be used for a scratch data area or for execution of code. As the name implies, the PMM is available only during POST. The duration of availability of the PMM to software components within the BIOS is implementation specific. Typically, BIOS clients can take advantage of the PMM as soon as memory initialization and testing is completed. For Option ROMs, the PMM is available when their initialization vector is called. The PMM is not available after the system BIOS has invoked INT 19h. All memory allocated by the PMM will be automatically zeroed and freed just before INT 19h is executed. The well-behaved Option ROM decides what it knows will not be used again. The PMM will be available when the Boot Connection Vector (BCV) is called for Option ROMs that have a SPdP header and contain a valid BCV. The PMM will not be available when the Bootstrap Entry Vector (BEV) is called, since this occurs after INT 19h is executed. Details of SPdP Option ROM initialization and booting may be found in the BIOS Boot Specification. ## 2.2 Clients of the PMM Clients of the PMM are both internal BIOS code modules and Option ROMs. Clients allocate memory blocks in paragraph (16-byte) chunks and free these blocks when they are done using them. The execution environment requires that the CPU be in real mode. The PMM Services provide a real mode far call interface only. Protected mode access is not currently defined. Option ROM clients should use BIOS interrupt 15h services to access extended memory. A client must not call interrupt 15h functions AH=87h or AH=89h, or modify Gate A20, as this may cause a catastrophic system failure. ``` #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 6 Context: # 3 PMM Services Interface Clients that access the PMM do so through a set of function calls collectively referred to as PMM Services. PMM Services reside within the system BIOS and are available to both internal BIOS clients as well as Option ROM clients once the presence of PMM Services has been detected. In a system BIOS implementation of the POST Memory Manager Specification, all three PMM Services function calls are required to be present. ## 3.1 Detecting PMM Services A data structure exists within the BIOS for PMM presence detection. The PMM Structure is located in the system BIOS address space on a paragraph boundary between segment addresses E000h and FFFFh. There will be only one PMM Structure in the system BIOS. The presence of the PMM Structure indicates that the PMM Services are present and available for calling. ### 3.1.1 PMM Structure | Offset | Name | Size | Value | Description | |--------|-------------------------|------|--------|------------------------------------| | 00h | SignatureByteOne | BYTE | ‘S’ | Signature byte 1. | | 01h | SignatureByteTwo | BYTE | ‘P’ | Signature byte 2. | | 02h | SignatureByteThree | BYTE | ‘M’ | Signature byte 3. | | 03h | SignatureByteFour | BYTE | ‘M’ | Signature byte 4. | | 04h | StructureRevision | BYTE | 01h | Structure revision. | | 05h | StructureLength | BYTE | Varies | Length of this structure in bytes. | | 06h | StructureChecksum | BYTE | Varies | Checksum update field. | | 07h | EntryPoint | FAR PTR | Varies | Segment:Offset of PMM Services entry point. | | 08h | Reserved | 5 BYTES | 0 | Reserved | The StructureRevision field will be incremented if the PMM Structure changes. All fields up to and including the EntryPoint field are guaranteed not to change location within the PMM Structure. Any change to the PMM Structure will be at or beyond the Reserved field only. ### 3.1.2 Detection Algorithm A client follows this procedure to locate and access PMM Services: 1. Search for the four-byte “SPMM” string on paragraph boundaries starting at E000h, and ending, if not found, at FFFFh. 2. Verify that the PMM Structure data is valid by performing a checksum. The checksum is calculated by doing a byte-wise sum of the entire PMM Structure and comparing this sum with zero. If the checksum is not zero, then the PMM Structure data is not valid and the EntryPoint field should not be called. 3. Optionally inspect the StructureRevision field to determine the appropriate structure map. The StructureRevision field changes if previously reserved fields in the PMM Structure are redefined to be valid fields. 4. Make calls to the EntryPoint field in the PMM Structure to allocate and free memory as desired. ## 3.2 PMM Services The functions described below define PMM Services. The calls are defined as if called from the C programming language. The PMM interface uses the standard C large model calling convention. In particular, return values of type unsigned long are returned in the DX:AX register pair. #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 7 Context: # 3.2.1 Interface Style The `EntryPoint` field in the PMM Structure points to the PMM Services interface. Clients call the PMM Services by executing a far call to the address specified in the `EntryPoint` field. PMM Services return to the client via a far return. Values returned to the caller are placed in the DX:AX register pair. The flags and all registers, other than DX and AX, are preserved across calls to PMM services. ## 3.2.2 Stack Requirements PMM Services requires a minimum stack size of 256 bytes. BIOSes that support the PMM must provide a stack size of at least 1KB when calling an initialization vector, a Boot Connection Vector, or a Bootstrap Entry Vector of a PCI or Plug ISA Option ROM. ## 3.2.3 Memory Block Alignment The default alignment of memory blocks is on a paragraph boundary. Alignment of a memory block on a particular memory boundary (larger than a paragraph) is supported by specifying the desired alignment in the `length` parameter of the `pmnAllocate` function. ## 3.2.4 Accessing Extended Memory This section specifies how clients should access extended memory blocks allocated by the PMM. When control is passed to an option ROM from a BIOS that supports PMM, the processor will be in real mode, and Gate A20 will be disabled (segment wrap turned off). This allows access to extended memory blocks using real mode addressing. In big real mode, access to memory above 1MB can be accomplished by using a 32-bit extended index register (EDI, etc.) and setting the segment register to 0000h. The following code example assumes that the `pmnAllocate` function was just called to allocate a block of extended memory, and DX:AX returned the 32-bit buffer address. ```assembly ; Assume here that DX:AX contains the 32-bit address of our allocated buffer. ; Clear the DS segment register. push 0000h pop ds ; Put the DX:AX 32-bit buffer address into EDI. mov di, dx ; Get the upper word. shl di, 16 ; Shift it to upper EDI. mov di, ax ; Get the lower word. ; Example: clear the first four bytes of the extended memory buffer. mov [edi], 00000000h ; D:EDI is used as the memory pointer. ``` In a similar way, the other segment registers and 32-bit index registers can be used for extended memory accessing. **WARNING:** Clients must not modify the state of Gate A20, put the processor in protected mode, or call BIOS Interrupt 15, functions 87h or 89h. Any of these actions could alter the big real mode of the processor and could lead to a catastrophic system failure. # 3.3 pmnAllocate - Function 0 ## 3.3.1 Description The `pmnAllocate` function attempts to allocate a memory block of the specified type and size, and returns the address of the memory block to the caller. The memory block is a contiguous array of paragraphs whose size is specified by the length parameter. The contents of the allocated memory block are undefined and must be initialized by the client. #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 8 Context: # 3.3.2 Function Prototype ```c unsigned long {entryPoint}( unsigned int function, // 0 for pmAllocate unsigned long length, // in paragraphs unsigned long handle, // handle to assign to memory block unsigned int flags // bit flags specifying options ); ``` ## 3.3.3 Parameters ### function Value for pmAllocate. Invalid values for the function parameter (0x0003…0xFFFF) cause an error value of 0xFFFFFFFF to be returned, signaling that the function is not supported. ### length The size of the requested memory block in paragraphs, or if length is 0x00000000, no memory is allocated and the value returned is the size of the largest memory block available for the memory type specified in the flags parameter. The alignment bit in the flags register is ignored when calculating the largest memory block available. ### handle A client-specified identifier to be associated with the allocated memory block. A handle of 0xFFFFFFFF indicates that no identifier should be associated with the block. Such a memory block is known as an “anonymous” memory block and cannot be found using the pmnFind function (see below). If a specified handle for a requested memory block is already used in a currently allocated memory block, the error value of 0x00000000 is returned. ### flags A bitmap used by the client to designate options regarding memory allocation. | Bits | Field | Value | Description | |-------|--------------|-------|-------------------------------------------------------| | 1.0 | MemoryType | 1.3 | 0 = Invalid | | | | | 1 = Requesting conventional memory block (0 to 1MB). | | | | | 2 = Requesting extended memory block (1MB to 4GB). | | | | | 3 = Requesting either a conventional or an extended memory block, whichever is available. | | 2 | Alignment | 0.1 | 0 = No alignment. | | | | | 1 = Use alignment from the length parameter. | | 15.3 | Reserved | 0 | Reserved for future expansion, must all be zero. | ### flags.MemoryType Specifies whether the requested memory block should be allocated from conventional memory, extended memory, or from either conventional or extended memory. At least one of the bits in this field must be set. If bit 0 is set, the PMM will attempt to allocate only from conventional memory. If bit 1 is set, the PMM will attempt to allocate only from extended memory. If both bits 0 and 1 are set, the PMM will allocate from either type. #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 9 Context: # Memory Management Functions ## 3.4 pmmFind - Function 1 ### 3.4.1 Description The `pmmFind` function returns the address of the memory block associated with the specified handle. The `entryPoint` identifier is of type function pointer, and is a far address. It must be assigned the value of the `EntryPoint` field in the PMM structure before it can be called. The return value is of type unsigned long and represents the 32-bit physical memory address of the memory block that is associated with the handle. A return value of `0x00000000` indicates that the handle does not correspond to a currently allocated memory block. ### 3.4.2 Function Prototype ```c unsigned long (*entryPoint) { unsigned int function, // 1 for pmmFind unsigned long handle // handle assigned to memory block }; ``` ### 3.4.3 Parameters - **function** 1 for `pmmFind`. Invalid values for the function parameter (`0x0003`...`0xFFFF`) cause an error value of `0xFFFFFFFF` to be returned, signaling that the function is not supported. - **handle** An identifier specified by the client that was assigned to a memory block when the `pmmAllocate` function was called. If called with an anonymous handle (`0xFFFFFFFF`) or a currently undefined handle, a value of `0x00000000` will be returned indicating that no associated memory block was found. ## 3.5 pmmDeallocate - Function 2 ### 3.5.1 Description The `pmmDeallocate` function frees the specified memory block that was previously allocated by `pmmAllocate`. Memory blocks are not cleared to all zeros by the PMM before being deallocated. Memory below 1MB is cleared by the BIOS before OS boot, but deallocated extended memory blocks in particular will not explicitly be cleared. Clients of the PMM should not rely on the contents of deallocated memory blocks. #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 10 Context: The entryPoint identifier is of type far function pointer. It must be assigned the value of the EntryPoint field in the PMM structure before it can be called. The return value is of type unsigned long. If the memory block was deallocated correctly, the return value is `0x00000000`. If there was an error, the return value is non-zero. The error return values are implementation dependent, so long as they are non-zero. ## 3.5.2 Function Prototype ```c unsigned long (*entryPoint) ( unsigned int function, // 2 for pmmDeallocate unsigned long buffer // value returned by pmnAllocate ); ``` ## 3.5.3 Parameters ### function 2 for `pmmDeallocate`. Invalid values for the function parameter (`0x0003...0xFFFF`) cause an error value of `0xFFFFFFFF` to be returned, signaling that the function is not supported. ### buffer The 32-bit physical address of the memory block. This is the value that was returned by the `pmmAllocate` function. #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 11 Context: # 3.6 C Language Coding Example The following C language code example illustrates how to use PMM Services. It finds the PMM entry point, allocates a 16KB conventional memory block, verifies the block is found by using its handle, and deallocates the memory block. No error checking is performed. ```c // Include library files: #include // Structure templates and type definitions: typedef unsigned char UCHAR; typedef unsigned int UINT; typedef unsigned long ULONG; typedef struct { ULONG signature; // PMM Structure UCHAR revision; UCHAR length; UCHAR checksum; UCHAR reserved[5]; } PMM_STRUCTURE; // Equate definitions: #define PMM_ALLOCATE 0 // PMM function numbers. #define PMM_FIND 1 #define PMM_DEALLOCATE 2 #define BUFFER_SIZE 1024 // Number of paragraphs for 16KB buffer. #define HANDLE 0x12345678 // Handle to test PMM. #define PMM_SIGNATURE 0x0ABCD5204 // "PMM" string in DWORD format. #define NULL_FUNCTION_PTR ((ULONG) 0) // Function declarations: void main(void); ULONG findPMMEntry(void); // Start of main program. void main(void) { ULONG (*pmmEntry[])() = { findPMMEntry(), result }; UCHAR buffer[BUFFER_SIZE], *buffer_alias; // Get PMM Services entry point, exit if not found. if ((pmmEntry[0] == NULL_FUNCTION_PTR)) exit(1); // Allocate a 16KB buffer and assign our own handle to it. buffer = (UCHAR *) (pmmEntry[PMM_ALLOCATE, BUFFER_SIZE, HANDLE]); // Look up the buffer address based on our known handle. buffer_alias = (UCHAR *) (pmmEntry[PMM_FIND, HANDLE]); // Deallocate the buffer. result = *(pmmEntry[PMM_DEALLOCATE, buffer]); } ``` #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 12 Context: ```markdown // Searches for the existence of PM Services and returns the entry point if found. ULONG FindEntry(VOID) { ULONG searchAddress; ULONG *entryPointer; UCHAR *checkSumPointer; PMB_STRUCTURE *pmStruct; DWORD i; UCHAR sum; // Search the DMG region on paragraph boundaries from E0000h to EFFFFh for "PMB". // If found, verify the checksum and return the entry point. // Else, return null. for (searchAddress = 0x000E0000; searchAddress < 0x000FFFFF; searchAddress++) { // Convert to an x86-style far pointer entryPointer = (ULONG *) (searchAddress << 16); if (entryPointer != (ULONG *) PMB_SIGNATURE) { // Found "PMB" signature, calculate the structure check sum. checkSumPointer = (UCHAR *) entryPointer; pmStruct = (PMB_STRUCTURE *) entryPointer; for (i = 0, sum = 0; i < sizeof(PMB_STRUCTURE); ++i) { sum += *checkSumPointer; ++checkSumPointer; } // Did the structure checksum verify? if (sum == 0) { // Yup, return the entry point. return (ULONG) pmStruct->entryPoint; } } } // Structure not found or didn't checksum correctly. return (NULL_FUNCTION_PTR); } ``` #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 13 Context: ```markdown # 3.7 Assembly Language Coding Example The following assembly language coding example performs the equivalent of the C language example above. Note the use of x86 instructions (pushd, etc.) and 32-bit instruction operands. ```assembly ; Find the PM Services entry point and checksum the structure. call findPMEntry ; DS:SI will point to PM structure. ; Allocate a 16KB buffer and assign our own handle to it. push 0031h ; Specify conventional memory. push 12345678h ; Our handle is 12345678h. push 00000400h ; Buffer size is 16K (40th paragraph). push 0000h ; Specify allocate - function 0. call DWORD PTR [esi + 77] ; Call the entry point in the structure. add sp, 12 ; Clean up stack after call - C style. cmp dx, 0000h ; Buffer address is in DX:AX (32-bit). jbe allocSuccess jmp failed ; Return value of 00000000h is an error. allocSuccess: ; Save our buffer address from DX:AX into EDI. mov di, dx ; Put the upper word into DI. shl di, 16 ; Shift it to upper EDI. mov di, ax ; Put the lower word into EDI. ; Look up the buffer address based on our known handle. push 12345678h ; Our handle is 12345678h. push 0031h ; Specify find - function 1. call DWORD PTR [esi + 77] ; Call the entry point in the structure. add sp, 12 ; Clean up stack after call - C style. cmp dx, 0000h ; Buffer address is in DX:AX (32-bit). jbe findSuccess jmp findFailed ; Return value is in DX:AX (32-bit). findSuccess: push edi ; Retrieve buffer address from EDI. push 002h ; Specify deallocate - function 2. call DWORD PTR [esi + 77] ; Call the entry point in the structure. add sp, 12 ; Clean up stack after call - C style. cmp dx, 0000h ; Return value is in DX:AX (32-bit). jbe passed jmp failed ; Return value of 00000000h is an error. passed: ; No error occurred. ``` ``` #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 14 Context: # 4. Conventions for Creating Memory Block Handles Although most clients of the PMM will typically use an anonymous handle (0xFFFFFFFF), the PMM supports named memory regions (via handles) which persist until deallocated or until POST completes. Since these services are available for use by Option ROMs, the possibility occurs that a handle allocated by the BIOS or by one vendor's Option ROM may be the same as the handle another vendor's Option ROM is designed to use. This section describes standard mechanisms which can avoid "handle collision." PMM implementations will not (and, in some cases, cannot) validate handles requested by Option ROMs for compliance with these conventions. Compliance with these conventions supports the best interests of the vendors and industry-wide interoperability. ## 4.1 Name Selection ### 4.1.1 EISA Product Identifiers The convention defined here relies on the use of Plug and Play / EISA Product Identifiers (“PnP ID’s”). These IDs consist of two parts. The first part is a manufacturer identifier of 3 uppercase English letters (A through Z) compressed into 16 bits, using 5 bits per character with 1 for “A” up to 26 for “Z.” The second part is a 16-bit value defined by the manufacturer. These two parts are concatenated together to form a 32-bit value: | First | second | third | fourth | byte | |-------|--------|-------|--------|-----------------------------------------------| | Aaaa | bb | bbb | cccc | most significant bit is zero
compressed first character
compressed second character
compressed third character | | nnnnnnnn | nnnnnnnn | manufacturer defined | | The values are generally written in documentation as the 3 ASCII characters followed by a hexadecimal digits, e.g., PNPC003. The manufacturer identifiers are unique and must be requested from BCPR Services, Inc. as described in the “EISA Specification Version 3.12”, BCPR Services, Inc., 1992. ### 4.1.2 Convention for Selecting Handle Values This specification requires that a card’s Option ROM must use handles that comply with the card manufacturer’s PnP ID. That is to say, the upper 16 bits of a handle are the manufacturer’s vendor identifier, whereas the vendor defines the lower 16 bits. The handles used by an Option ROM are not required to correspond to any IDs of the cards on which the Option ROMs making the requests reside. For example, if a PnP ISA card has a PnP ID of “XYZ000”, its Option ROM may validly make a request for memory with the handle “XYZ000” or “XYZ123” but not “ABC0000”. This distinction between the card’s ID and handle values (while still retaining association with the vendor via the vendor identifier) has several consequences. For example, it provides the flexibility for this convention to support Option ROMs for cards that do not have PnP IDs (for example PCI cards). Also, a card may allocate multiple named blocks without having multiple PnP IDs. The BIOS reserves for itself all IDs with the high order bit set and any ID with the high-order 6 bits equal to zero. The BIOS may or may not flag attempts by Option ROMs to use these ids as an error. #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 15 Context: # Disclaimer In cases where add-in cards and Option ROM code is purchased from one company for distribution by a different company, the exact definition of who is the manufacturer of a particular peripheral may become murky. It is up to the various parties involved to establish their own practices within the guidelines of this document. A case where this may arise is a vendor of Option ROMs selling those to several card manufacturers. Note that Option ROMs may allocate anonymous handles without fear of colliding with previously allocated handles. ## 4.2 Recommended Method for the Use of Named Blocks In general, only one named region is needed per card. The named block is allocated and used as a list of pointers to unnamed blocks, functioning somewhat like a directory in a file system. As an example, assume that the hard disk controller card marketed by XYZ has an Option ROM. During its initialization process, the card's Option ROM needs to allocate a buffer for reading from the hard disks it controls and a linked list of blocks describing each possible bootable hard disk. The following pseudo code would implement this scenario. (Error recovery, while important, is not shown for the sake of clarity.) ```c #define NoId 0xFFFFFFFF // The ID passed in for anon blocks. #define XYZ20000 ( ((‘Y’^A+1) << 26) | \ ((‘Y’^A+1) << 21) | \ ((‘Z’^A+1) << 16) ) #define ConvMem 1 // allocate blocks between 0 and 640k struct dir { byte *buffer; struct BootStruct *boot; }; struct dir *base, *t; // readability function unsigned long pmmlAllocate (unsigned long handle, size_t) { entryPoint = findPMEntry(); return (*entryPoint)(PMM_ALLOCATE, size, handle, ConvMem); } // (n+15)/16 calculates the required number of paragraphs given // an equal to the structure's length in bytes. base = pmmlAllocate(XYZ20000, (sizeof(dir)+15)/16); // allocate the buffer. base->buffEer = pmmlAllocate(NoId, (BufLen+15)/16); base->boot = NULL; for each device i { // do stuff if (device is bootable) { t = pmmlAllocate(NoId, (sizeof(BootStruct)+15)/16); t->next = base->boot; base->boot = t; } } ``` This means that, in general, only one named block is required per Option ROM. (The value of XYZ20000 is 0x633A0000.) Visually, the resulting data structure is: #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 16 Context: # Support of Multiple Structures With One Named Block ### XYZ0000 ``` Dir | Buffer | |--- BootStruct | |--- BootStruct ``` ### Key - **Anonymous Block** - **Named Block** - **Null Pointer** ## 4.3 Finding Other Cards in the System The use of PnP IDs for named handles has another consequence: it makes it possible to perform a simplified search for other similar cards in the system. Assume that XYZ's Option ROMs always attempt to allocate a block of memory named “XYZ0000”. If two of XYZ's Option ROMs exist in the same system, the second request to allocate will fail. If the company's Option ROMs instead do a `pmmFind` for the handle XYZ0000 prior to attempting to allocate memory with the same handle, the Option ROM can use the previously initialized allocation. In companies with more complex product lines, each set of products may need to use a different “family” ID (e.g., XYZ0000 for one family, XYZ0100 for another, etc.). Pseudocode implementing multiple card support is shown below. ```c #define NoId 0xFFFFFFFF #define XYZ0000 ((*(Y*++)+1) << 26) | \ ((*(Y*++)+1) << 21) | \ ((*(Y*++)+1) << 16) struct dir { struct dir *nextcard; // point to next directory byte *buffer; struct BootStruct *boot; int cardsCS; } dir; struct dir *base, *t; // pmmAllocate is assumed to be defined as above. if ((base = (*entryPoint)(PMM_FIND, XYZ0000)) != 0) { t = pmmAllocate(NoId, (sizeof(dir) + 15) / 16); t->nextcard = base->nextcard; base = t; } ``` #################### File: BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf Page: 17 Context: ```markdown } else { base = pmAllocate(XYZ0000, (sizeof(dir)+15)/16); base->nextcard = NULL; } ... base->buffer = pmAllocate(NoId, (BufLen+15)/16); base->cardsCS = GetOurCodeSegment(); // base->boot = NULL; for each device i { // do stuff if (device is bootable) { t = pmAllocate(NoId, (sizeof(BootStruct)+15)/16); t->next = base->boot; base->boot = t; } } Visually, the resulting data structure looks like this: # Support of Multiple Cards With One Named Block ``` XYZ0000 ├── Dir │ ├── 0D000h (Card 1) └── Dir ├── 0D800h (Card 2) ├── Buffer └── Buffer ├── BootStruct └── BootStruct ``` ## Key - `[` Anonymous Block `]` - `---` Named Block - `->` Null Pointer [ End of Specification ] ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 1 Context: # 10. The Network ## 10.1 Introduction Workstations are typically, but not always, connected in a local environment by a network. There exist two basically different views of the architecture of such nets. The more demanding view is that all connected stations constitute a single, unified workspace (also called address-space), in which the individual processors operate. It implies the demand that the "thin" connections between processors are hidden from the users. At worst they might become apparent through slower data access rates between the machines. To hide the difference between access within a computer and access between computers is regarded primarily as a challenge to implementors. The second, more conservative view, assumes that individual workstations are, although connected, essentially autonomous units which exchange data infrequently. Therefore, access of data on partner stations is initiated by explicit transfer commands. Commands handling external access are not part of the basic system, but rather are implemented in modules that might be regarded as applications. In the Oberon System, we adhere to this second view, and in this chapter, we describe the module **Net**, which is an autonomous command module based on the network driver SCC. It can be activated on any station connected in a network, and all of them are treated as equals. Such a set of loosely coupled stations may well operate in networks with moderate transmission rates and therefore with low-cost hardware interfaces and twisted-pair wires. An obvious choice for the unit of transferred data is the file. The central theme of this chapter is therefore file transfer over a network. Some additional facilities offered by a dedicated server station will be the subject of Chapter 11. The commands to be presented here are a few only: `SendFiles`, `ReceiveFiles`, and `SendMsg`. As explained in Chapter 2, Oberon is a single-process system where every command monopolizes the processor until termination. When a command involves communication over a network, (at least) two processors are engaged in the action at the same time. The Oberon paradigm therefore appears to exclude such cooperation; but fortunately it does not, and the solution to the problem is quite simple. Every command is initiated by a user operating on a workstation. For the moment we call it the **master** (of the command under consideration). The addressed station - obviously called the **server** - must be in a state where it recognizes the command in order to engage in its execution. Since the command - called a **request** - arrives in encoded form over the network, an Oberon task represented by a handler procedure must be inserted into the event polling loop of the system. Such a handler must have the general form: ``` IF event present THEN handle event END ``` The guard, in this case, must imply that a request was received from the network. We emphasize that the event is sensed by the server only after the command currently under execution, if any, has terminated. However, data arrive at the receiver immediately after they are sent by the master. Hence, any sizeable delay is inherently inadmissible, and the Oberon metaphor once again appears to fail. It does not fail, however, because the unavoidable, genuine concurrency of sender and receiver action is handled within the driver module which places the data into a buffer. The driver is activated by an interrupt, and its receiver buffer effectively decouples the partners and removes the stringent timing constraints. All this remains completely hidden within the driver module. ## 10.2 The Protocol If more than a single agent participates in the execution of a command, a convention must be established and obeyed. It defines the set of requests, their encoding, and the sequence of data. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 2 Context: # Protocol Definition In this section, we define a protocol for the exchanges that follow. This convention is used to describe actions initiated by the master and the server. The protocol can be defined using EBNF (Extended Backus-Naur Form), well-known from the syntax specification of programming languages. Items originating from the master will be written in *normal* font, while items from the server appear in *italics*. ## ReceiveFile Request A simple form of the `ReceiveFile` request is defined as follows and will be refined subsequently: ``` ReceiveFile = SND filename (ACK data | NAK). ``` Here, the symbol `SND` represents the encoded request that the server send the file specified by the filename. `ACK` signals that the request is honored and the requested data follows. The `NAK` symbol indicates that the requested file cannot be delivered. The transaction clearly consists of two parts: the request and the reply, one from each partner. This simple-minded scheme fails because of the limitations of the size of each transmitted portion imposed by the network driver. We recall that module SCC restricts the data of each packet to 512 bytes. Evidently, files must be broken up and transmitted as a sequence of packets. The reason for this restriction is transmission reliability. The break-up allows the partner to confirm correct receipt of a packet by returning a short acknowledgment. Each acknowledgment also serves as a request for the next packet. An exception is the last acknowledgment following the last data portion, which is characterized by its length being less than the admitted maximum. The revised protocol is defined as: ``` ReceiveFile = SND filename (DAT data | ACK | NAK). ``` We now recall that each packet is defined as follows: ``` Packet = SND | DAT | ACK | NAK. ``` The symbols `SND`, `DAT`, `ACK`, and `NAK` indicate this packet type. The data portions of `ACK` and `NAK` packets are empty. ### Error Handling The revised protocol fails to cope with transmission errors. Correct transmission is checked by the driver through a cyclic redundancy check (CRC), and an erroneous packet is simply discarded. This implies that a receiver must impose a timing constraint. If an expected packet fails to arrive within a given time period (timeout), the request must be repeated. In our case, a request is implied by an acknowledgment. Hence, the acknowledgment must specify whether the next (normal case) or the previously requested (error case) packet must be sent. The solution is to attach a sequence number to each acknowledgment and to each data packet. These numbers are taken modulo 8, although in principle modulo 2 would suffice. With the addition of a user identification and a password to every request, and of an alternate reply code NRP for "no permission", the protocol reaches its final form: ``` ReceiveFile = SND username password filename (datastream | NAK | NRP). datastream = DAT data ACK | {DAT data ACK...}. ``` The protocol for file transmission from the master to the server is defined similarly: ``` SendFile = REC username password filename (ACK datastream | NAK | NRP). datastream = DAT data ACK | {DAT data ACK...}. ``` The third request listed above, `SendMsg`, does not refer to any file, but merely transmits and displays a short message. It is included here for testing the link between two partners and perhaps for visibly acknowledging a rendered service by the message "done" or "thank you". ``` SendMsg = MSG message ACK. ``` ## 10.3 Station Addressing Every packet must carry a destination address as well as the sender's address. Addresses are station numbers. It would certainly be inconvenient for a user to remember the station number of a desired partner. Instead, the use of symbolic names is preferred. We have become accustomed to use the partner's initials for this purpose. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 3 Context: The source address is inserted automatically into packet headers by the driver. It is obtained from a DIP switch set when a computer is installed and connected. But where should the destination address come from? From the start, we reject the solution of an address table in every workstation because of the potential inconsistencies. The concept of a centralized authority holding a name/address dictionary is equally unattractive, because of the updates required whenever a person uses a different computer. Also, we have started from the premise to keep all participants in the network equal. The most attractive solution lies in a decentralized name service. It is based on the broadcast facility, i.e., the possibility to send a packet to all connected stations, bypassing their address filters with a special destination address (-1). The broadcast is used for emitting a name request containing the desired partner's symbolic name. A station receiving the request returns a reply to the requester, if that name matches its own symbolic name. The requester then obtains the desired partner's address from the source address field of the received reply. The corresponding simple protocol is: ``` NameRequest = NRQ partnername [NRS]. ``` Here, the already mentioned timeout facility is indispensable. The following summarizes the protocol developed so far: ``` protocol = {request}. request = ReceiveFile | SendFile | SendMsg | NameRequest. ``` The overhead incurred by name requests may be reduced by using a local address dictionary. In practice, a single entry is satisfactory. A name request is then needed whenever the partner changes. ## 10.4 The Implementation Module `Net` is an implementation of the facilities outlined above. The program starts with a number of auxiliary, local procedures. They are followed by procedure `Serve` which is to be installed as an Oberon task, and the commands `SendFile`, `ReceiveFiles`, and `SendMsg`, each of which has its counterpart within procedure `Serve`. At the end are the commands for starting and stopping the server task. For a more detailed presentation we select procedure `ReceiveFiles`. It starts out by reading the first parameter which designates the partner station from the command line. Procedure `FindPartner` issues the name request, unless the partner's address has already been determined by a previous command. The global variable `partner` records a symbolic name (ID) whose address is stored in the destination field of the global variable `head`, which is used as header in every packet sent by procedure `SCC.SendPacket`. The variable `partner` may be regarded as a name cache with a single entry and with the purpose of reducing the number of issued name requests. If the partner has been identified, the next parameter is read from the command line. It is the name of the file to be transmitted. If the parameter has the form `name@name`, the file stored on the server as `name.name` is fetched and stored locally as `name`. Hence, `name` serves as a prefix of the file name on the server station. Thereafter, the requested parameters are concatenated in the local buffer variable `buf`. They are the user’s name and password followed by the file name (user name and password remain unused by the server procedures defined here). The command package is dispatched by the call `Send(SND, buf)`, where `buf` denotes the length of the command parameter string. Then, the reply packet is awaited by calling `ReceiveHead`. If the received packet's type is `DAT` with sequence number 0, a new file is established. Procedure `ReadData` receives the data and stores them in the new file, obeying the protocol defined in Section 10.2. This process is repeated for each file specified in the list of file names in the command line. ### Procedure `ReceiveHead()` Receives packets and discards them until one arrives from the partner from which it is expected. The procedure represents an input filter in addition to the one provided by the protocol. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 4 Context: # Data Transmission Procedures The hardware discriminates on the basis of the packets' source address, whereas the hardware filter discriminates on the basis of the destination address. If no packet arrives within the allotted time `T`, a type code `-1` is returned, signifying a timeout. ## Procedure ReceiveData The `ReceiveData` checks the sequence numbers of incoming data packets (type 0 - 7). If an incorrect number is detected, an ACK packet with the previous sequence number is returned (type 16 - 23), requesting a retransmission. At most two retries are undertaken. This seems to suffice, considering that also the server does not accept any other requests while being engaged in the transmission of a file. The part corresponding to `ReceiveFiles` within procedure `Serve` is guarded by the condition `head.tpe = SND`. Variable `head` is the recipient of headers whenever a packet is received by `ReceiveHead`. First, the request's parameters are scanned. `Id` and `pw` are ignored. Then the requested file is opened. If it exists, the transmission is handled by `ReceiveData`'s counterpart, procedure `SendData`. The time limit for receiving the next data packet is `T1`, whereas the limit of number of possible (re)transmissions is `T0`. `T1` is roughly `70` multiplied by the maximum data wait until no further retransmission requests can be expected to arrive. The value `T0 (300)` corresponds to `1`; the time for transmission of a packet of maximum length is about `16ms`. ## Procedure SendFiles Procedure `SendFiles` is designed analogously; its counterpart in the server is guarded by the condition `head.tpe = REC`. The server accepts the request only if its state is unprotected (global variable `protected`). Otherwise, the request is negatively acknowledged with an NPR packet. We draw attention to the fact that procedures `SendData` and `ReceiveData` are both used by command procedures as well as by the server. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 5 Context: # 11. A Dedicated File-Distribution and Mail-Server ## 11.1. Concept and Structure In a system of loosely coupled workstations, it is desirable to centralize certain services. A first example is a common file store. Even if every station is equipped with a disk for permanent data storage, a common file service is beneficial, e.g., for storing the most recent versions of system files, reference documents, reports, etc. A common repository avoids inconsistencies which are inevitable when local copies are created. We call this a **file distribution service**. A centralized service is also desirable if it requires equipment whose cost and service would not warrant its acquisition for every workstation, particularly if the service is infrequently used. A prime example of this case is a **printing service**. The third case is a communication facility in the form of **electronic mail**. The repository of messages must inherently be centralized. We imagine it to have the form of a set of mailboxes, one for each user in the system. A mailbox needs to be accessible at all times, i.e., also when its owner’s workstation has been switched off. A last example of a centralized service is a **time server**. It allows a station's real time clock to be synchronized with a central clock. In passing, we point out that every user has full control over his station, including the right to switch it on and off at any time. In contrast, the central server is continuously operational. In this chapter, we present a set of server modules providing all the above-mentioned services. They rest on the basic Oberon System without module Net (see Chapter 10). In contrast to **Net**, module **NetServer**, which handles all network communication, contains no command procedures (apart from those for starting and stopping it). This is because it never acts as a master. The counterparts of its server routines reside in other modules, including an extended version of **Net**, on the individual workstations. Routines for the file distribution service are the same as those contained in module **Net**, with the addition of permission checks based on the received user names and passwords. Routines for printing and mail service could in principle also be included in **NetServer** in the same way. But considerations of reliability and timing made this simple solution appear unsatisfactory. A weaker coupling in line of data transmission and data consumption is indeed highly desirable. Therefore, files received for printing or for dispatching into mailboxes are stored (by **NetServer**) in temporary files and thereafter "handed over" to the appropriate agent, i.e., the print server or the mail server. This data-centered interface between servers—in contrast to procedural interfaces—has the advantage that the individual servers are independent in the sense that none imports any other. Therefore, their development could proceed autonomously. Their connection is instead a module that defines a data structure and associated operators for passing temporary files from one server to another. The data structure used for this purpose is the first-in-first-out queue. We call its elements tasks, because each one carries an objective and an object, the file to be processed. The module containing the FIFOs is called **Core**. The resulting structure of the involved modules is shown in Figure 11.1. ![Figure 11.1](link_to_figure) Figure 11.1 includes another server, **LineServer**, and shows the ease with which additional servers may be inserted in this scheme. They act as further sources and/or sinks for tasks, feeding or consuming the queues contained in **Core**. **LineServer** indeed produces and consumes tasks like **NetServer**. Instead of the RS-485 bus, it handles the RS-232 line which, connected to a modem, allows access to the server over telephone lines. We refrain from describing this module in further detail, because in many ways it is a mirror of **NetServer**. A centralized, open server calls for certain protection measures against unauthorized use. We recall that requests always carry a user identification and a password as parameters. The server #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 6 Context: ``` checks their validity by examining a table of users. The respective routines and the table are contained in module Core (see Sect. 11.5). ``` **Figure 11.1** Module structure of server systems | | | | | | |---------|---------------|-------------|-----------|----------| | NetServer | LineServer | PrintServer | MailServer | Core | | SCC | RS232 | PrintMaps | | | ## 11.2. Electronic Mail Service The heart of an e-mail service is the set of mailboxes stored on the dedicated, central server. Each registered user owns a mailbox. The evidently necessary operations are the insertion of a message and its retrieval. In contrast to customary letter boxes, however, a retrieved message need not necessarily be removed from the box; its retrieval produces a copy. The box thereby automatically becomes a repository, and messages can be retrieved many times. This scheme calls for an additional command which removes a message from the box. Also, a command is needed for delivering a table of contents, in which presumably each message is represented by an indication of its send time and time of arrival. The mail scheme suggested above results in the following commands: - `Net.Mailbox ServerName`. This command fetches a table of contents of the current user's mailbox from the specified server and displays it in a new viewer. The user's name and password must have been registered previously by the command `System.SetUser`. - `Net.SendMail ServerName`. The text in the marked viewer is sent to the specified server. In order to be accepted, the text must begin with at least one line beginning with "To:" and containing at least one recipient. - `Net.ReceiveMail`. This command is contained in the title bar (menu) of the viewer abandoned when requesting the table of contents. Prior to issuing the command, the message to be read must have been specified by selecting a line in the table of contents in this viewer. This mail system presented here is primarily intended to serve as an exchange for short messages which are typically sent, received, read, and discarded. Mailboxes are not intended to serve as long term archives for a large and ever-growing number of long pieces of text. This restrictiveness of purpose allows to choose a reasonably simple implementation and results in efficient, practically instantaneous access to messages when the server is idle. The Oberon mail server used at ETH also provides communication with external correspondents. It connects to an external mail server which is treated as a source and a sink for messages (almost) like other customers. Additionally, messages sent to that server need to be encoded into a standardized format, and those received need to be decoded accordingly. The parts of module `MailServer` for encoding and decoding are not described in this book. We merely outline the design and implementation took a multiple of the time spent on the last, local message exchange, to which we confine this presentation. From the structures explained in Section 11.1, it follows that three agents are involved in the transfer of messages from the user into a mailbox. Therefore, additions to the server system distribute over three modules. New commands are added to module `Net` (see Section 10.4); these procedures will be listed below. Their counterparts reside in module `NetServer` on the dedicated server. ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 7 Context: # Path of messages to and from mailbox ![Figure 11.2 Path of messages to and from mailbox](path/to/your/image) Communication between the master station and the dedicated server runs over the network and therefore calls for an extension of its protocol (see Sect. 10.2). The additions directly correspond to the four commands given above. - **MailBox** → MDR username password (datastream | NAK | NPR). - **SendMail** → RML username password (ACK | datastream | NAK | NPR). - **ReceiveMail** → SML username password msgon (datastream | NAK | NPR). - **DeleteMail** → DL username password msgon (ACK | NAK | NPR). The message number is taken from the selected line in the mailbox viewer. The data transmitted are taken as (unfortunately) texts. This is in contrast to file transfers, where they are taken as any sequence of bytes. The four command procedures listed below belong to module `Net`, they are listed together with the auxiliary procedures `SendText` and `ReceiveText` which closely correspond to `SendData` and `ReceiveData` (see Sect. 10.4). We now turn our attention to the command procedures' counterparts in module `NetServer` listed in this section. In order to explain these routines, a description of their interface with the mail server and a definition of the structure of mailboxes must precede. We begin with the simplest case, the counterpart of `SendMail`. It is the part of procedure `NetServer.Serve` which is guarded by the condition of `msg = 1`, indicating a request to receive mail. As in all other services, the parameters username and password are read and the admissibility of the request is checked. The check is performed by procedure `Core.UserNo` which yields a negative number if service is to be refused. In the affirmative case, procedure `ReceiveData` obtains the message and stores it on a file, which is thereafter inserted into the mail queue as a task to be handled by the mail server at a later time. This may involve distribution of the message to several mailboxes. Module `Core` is listed in Sect. 11.5. As mentioned before, it serves as a link between the various server modules, defining the data types of the linking queues and also of mailboxes. Task queues are represented as FIFO-lists. The descriptor of type `Queue` contains a pointer to the first list element used for retrieval, and a pointer to the last element used for insertion (see Fig. 11.3). These pointers are not exported; instead, the next task is obtained by calling procedure `Core.GetTask`, and it is deleted by `Core.RemoveTask`. There exist two exported variables of type `Queue`: `MailQueue` consumed by `MailServer`, and `PrintQueue` consumed by `PrintServer` (see Sect. 11.3). In fact, we use a third queue: `LineQueue` consumed by `LineServer`. Elements of queues are of type `TaskDesc` which specifies the file representing the task to be consumed. Additionally, it specifies the user number and identification of the task's originator. The procedures are provided by module `Core` for handling task queues: ```pascal PROCEDURE InsertTask(VAR Q: Queue; F: Files; VAR id: ARRAY OF CHAR; un: INTEGER); PROCEDURE GetTask(VAR Q: Queue; VAR F: Files; VAR id: ARRAY OF CHAR; VAR un: INTEGER); PROCEDURE RemoveTask(VAR Q: Queue); ``` The server's counterparts of the remaining mail commands access mailboxes directly. The simplicity of the required actions - a result of a carefully chosen mailbox representation - and considerations of efficiency do not warrant a detour via task queue and mail server. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 8 Context: # Queue ``` last | v +---+ | n | | 3 | +---+ | v +---+ | | +---+---+---+ | | v v 8 15 | id | jg | hm | rw +---+---+---+ | v NIL ``` **Figure 11.3 Structure of task queue** Every mailbox is represented as a file. This solution has the tremendous advantage that no special administration has to be introduced to handle a reserved partition of disk store for mail purposes. A mailbox file is partitioned into three parts: the block reservation part, the directory part, and the message part. Each part is quickly locatable, because the first two have a fixed length (32 and 31*32 = 992 bytes). The message part is regarded as a sequence of blocks (of 256 bytes), and each message occupies an integral number of adjacent blocks. Corresponding to each block, the block reservation part contains a single bit indicating whether or not the block is occupied by a message. Since the block reservation part is 32 bytes long, the message part contains at most 256 blocks, i.e. 64K bytes. The block length was chosen after an analysis of messages which revealed that the average message is less than 500 bytes long. The directory part consists of an array of 31 elements of type `MailEntry`, a record with the following fields: - `pos` and `len` indicate the index of the message's first block and the message's number of bytes; - `time` and `date` indicate the message's time of insertion, and `originator` indicates the message's source. The entries are linked (field `next`) in chronological order of their arrival, and entry 0 serves as the list's head. It follows that a mailbox contains at most 30 messages. An example of a mailbox table is shown in Figure 11.4. ## MailEntry ``` MAILEntry = RECORD pos: next: INTEGER; len: LONGINT; time: INTEGER; date: ARRAY 20 OF CHAR; END; ``` ## MResTab ``` MResTab = ARRAY 31 OF SET; MailDir = ARRAY 31 OF MailEntry; ``` We are now in a position to implement the handler for requests for message retrieval. It is guarded by the condition `top := SML`. After a valid check, the respective requestor's mailbox file is opened. The mailbox opened is related by the global variable `MF` which acts as a single entry cache. The associated user number is given by the global variable `mailno`. Since typically several requests involving the same mailbox follow, this measure avoids the repeated reopening of the same file. Thereafter, a rider is directly positioned at the respective directory entry for reading the message's length and position in the message part. The rider is repositioned accordingly, and transmission of the message is handled by procedure `SendMail`. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 9 Context: # Block reservation part ``` 1 100000010111110111111 ``` # Directory part | pos | len | time | date | orig | next | |-----|-----|---------|---------|-------------|--------| | 0 | 0 | 8.92 | 10:12 | 15.21 | Muller | | 1 | 2 | 15.97 | 11:27.2 | 17.19 | Templ | | 12 | 20 | 2.00 | 23:41 | 6.61 | Franz | # Message part ``` 0 2 8 15 ``` **Figure 11.4 State of mailbox file** Requests for the mailbox directory are handled by the routine guarded by the condition `typ = MDIR`. The directory part must be read and converted into a text. This task is supported by various auxiliary procedures (`Append`) which concatenate supplied data in a buffer for latter transmission. We emphasize that this request does not require the reading of any other part of the file, and therefore is very swift. The last of the four main service requests (DML) deletes a specified message. Removal from the directory requires a relinking of the entries. Unused entries are marked by their `len` field having a value of 0. Also, the blocks occupied by the message become free. The block reservation part must be updated accordingly. In passing, we note that the use of files for representing mailboxes, in combination with the file distribution services residing on the same server station, allows anyone to access (and inspect) any mailbox. Although we do not claim that this system provides secure protection against snooping, a minimal effort for protection was undertaken by a simple encoding of messages in mailbox files. This encoding is not shown in the program listings contained in this book. One operation remains to be explained in more detail: the processing of tasks inserted into the mail queue. It consists of the insertion of the message represented by the task's file into one or several mailboxes. It involves the interpretation of the message's header, i.e., lines containing addresses, and the construction of a new header containing the name of the originator and the date of insertion into the mailbox. These actions are performed by procedures in module `MailServer`. Its procedure `Serve` is installed as an Oberon Task, and it is guarded by the condition `Core.MailQueue.n > 0`, indicating that at least one message needs to be dispatched. The originator's name is obtained from `Core.GetUserName(uno)`, where `uno` is the user number obtained from the queue entry. The actual time is obtained from `Oberon.GetClock`. The form of the new header is shown by the following example: **From:** Gutknecht **Date:** 12.08.91 09:34:15 The received message's header is then searched for recipients. Their names are listed in header lines starting with "To:" (or "Cc:"). After a name has been read, the corresponding user number is obtained by calling `Core.UserNum`. Then the message is inserted into the designated mailbox by procedure `Dispatch`. The search for recipients continues, until a line is encountered that does not begin with "To:" (or "Cc:"). A negative user number indicates that the given name is not registered. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 10 Context: this case, the message is returned to the sender, i.e., inserted into the mailbox of the sender. An exception is the recipient "all" which indicates a broadcast to all registered users. ### Procedure Dispatch First opens the mailbox file of the user specified by the recipient number. If a mailbox exists, it blocks reservation part (matal) and its directory part (mdir) are read. Otherwise, a new, empty box is created. Then follows the search for a free slot in the directory and, if found, the search for a sufficient number of free, adjacent blocks in the message part. The number of required blocks is given by the message length. If either no free slot exists, or there is no large enough free space for the message part, the message is returned to the sender (identified by no). If also this attempt fails, the message is redirected to the postmaster (with user number 0). The postmaster is expected to inspect his mailbox sufficiently often so that no overflow occurs. If the postmaster’s mailbox also overflows, the message is lost. Only if all conditions for a successful completion are satisfied, is insertion begun. It starts with the marking of blocks in the reservation table and with the insertion of the new directory information. Table and directory are then updated on the file. Thereafter, the message with the constructed new header is written into the message part. Perhaps it may seem to the reader that the addition of a separate module **Mail/Server**, together with a new Oberon Task and the machinery of the mail queue is not warranted by the relative simplicity of the insertion operation, and that it could have been incorporated into module **Net/Server** just as well as message extraction. The picture changes, however, if handling of external mail is to be added, and if access to mailboxes via other channels, such as the RS-232 line, is to be provided. The presented solution is based on a modular structure that facilitates such extensions without change of existing parts. External mail routines inevitably have to cope with message formats imposed by standards. Format transformations, encoding before sending to an external server and decoding before dispatching become necessary. Indeed, these operations have inflated module **Mail/Server** in a surprising degree. And lastly, the queuing machinery supports the easy insertion of additional message sources and provides a welcome decoupling and relaxation of timing constraints, particularly in the case of low-speed transmission media such as telephone lines. ## 11.4. Miscellaneous Services There are a few additional services that are quite desirable under the presence of a central facility, and at the same time easy to include. They are briefly described in this section. The set of commands of the file distribution service is augmented by **Net.DeleteFiles** and **Net.Directory**, allowing the remote deletion of files and inspection of the server’s directory. The command procedures are listed below and must be regarded as part of module **Net** (Sect. 10.4). They communicate with their counterparts in module **Net/Server** (Sect. 11.2), according to the following protocol: - **DeleteFile** `DEL username password filename (ACK | NAK | NRP).` **Directory** `FDIR username password prefix (datastream | NAK | NRP).` The directory request carries a prefix; it uses procedure **FileDir.Enumerate** to obtain all file names starting with the specified prefix. Thereby the search can be limited to the relevant section of the directory. Since requests to the server are always guarded by a password, a facility is necessary to set and change the password stored by the server. The respective command is **Net.SetPassword**, and its handler in the server is guarded by the condition `p = NWP`. The corresponding protocol is: - **NewPassword** `NPW username oldpassword (ACK | NAK | NRP).` Finally, procedure **Net.GetTime** allows the workstation's real time clock to be adjusted to that of the central server. The protocol is: - `GetTime = TRQ TIM date time.` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 11 Context: # Summary of Protocol: ## Protocol | Protocol | Request | |-------------------|--------------------------------------------------------| | | ReceiveFile | SendFile | DeleteFile | Directory | | | MailBox | SendMail | ReceiveMail | DeleteMail | | | PrintStream | Sending | NameRequest | NewPassword | GetTime | ### ReceiveFile - SND username password filename (datastream | NAK | NPN). - datastream = DATA data ACK1 (DATA data ACK1:). ### SendFile - REC username password filename (ACK0 datastream | NAK | NRP). - datastream = DATA data ACK1 (DATA data ACK1:). ### DeleteFile - DEL username password filename (ACK | NAK | NRP). ### Directory - FDIR username password prefix (datastream | NAK | NRP). ### MailBox - FDIR username password (datastream | NAK | NRP). ### SendMail - RML username password (ACK datastream | NAK | NRP). ### ReceiveMail - SML username password msgno (datastream | NAK | NRP). ### DeleteMail - DML username password msgno (ACK datastream | NAK | NRP). ### PrintStream - PRT username password (ACK datastream | NAK | NRP). ### SendMsg - MSG message ACK. ### NameRequest - NRQ username (NRS). ### NewPassword - NWP username password (ACK DAT newpassword (ACK | NAK | NRP)). ### GetTime - TKN TIM time date. ## 11.5 User Administration It appears to be a universal fact that centralization inevitably calls for an administration. The centralized mail and printing services make no exception. The typical duties of an administration are accounting and protection against misuse. It has to ensure that registered services are counted and that an unauthorized user is taking advantage of the server. An additionally obvious item is that of statistical data. In our case, accounting plays a very minor role, and the reason for the existence of the administration presented here is primarily protection. We distinguish between two kinds of protection. The first is protection of the server's resources in general, the second is that of individual users' resources from being accessed by others. Whereas in the first case some validation of a user’s identification might suffice, the second case requires the association of personal resources with user names. In any case, the central server must store data for each member of the set of registered users. Specifically, it must be able to check the admissibility of a user's request on the basis of stored information. Evidently, a protection administration is similar in purpose and function to a lock. Quite regularly, locks are subjected to attempts of breaking them, and locks can also be subjected to attempts of being outwitted. The race between techniques of breaking locks and that of better countermeasures is well known, and we do not even try to make a contribution to it. Our design is based on the premise that the Oberon Server operates in a harmonious environment. Nevertheless, a minimal amount of protection machinery was included. It raises the amount of effort required for breaking protection to a level which is not reached when curiosity alone is the motivation. The data about users is held in a table in module Core. As mentioned earlier, Core acts as the connector between the various servers by means of task queues. Its designed purpose is to provide the necessary access to user data via appropriate procedures. In the simplest solution, each table entry would contain a user name only. For each request, the administration would merely test for the presence of the request's user name in the table. A significant step towards safe protection is the introduction of a password in addition to the user name. In order that a request be honored, not only must the name be registered, but the delivered and the stored password must match. Evidently, abusive attempts would aim at recovering the #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 12 Context: # User Identification and Password Management Stored passwords are managed through our solution that stores an encoded password. The command `System.SetUser`, which asks for a user identification and a password, immediately encodes the password. The original is stored nowhere; the encoding algorithm is such that it is difficult to construct a corresponding decoder. ## User Identification The mail service requires a third attribute in addition to identification and encoded password: the user's name as it is used for addressing messages. Identification typically consists of the user's initials; for the name, we suggest the full last name of the user and disambiguate cryptic abbreviations. ## Printing Service The printing service makes accounting facility desirable. A fourth field in each user table entry serves as a count for the number of printed pages. As a result, there are four fields: `id`, `name`, `password`, and `count`. The table is not exported but only accessible via procedures. `Core` is a good example of a resource hiding module. The program is listed below, and a few additional comments follow here. ### Procedures - `UserNo(id)` and `UserNum(name)` yield the table index of the identified user; it is called the user number and is used as a short encoding for recipients and senders within the mail server. In other servers, the number is merely used to check a request’s validity. ### User Information User information must certainly survive any interruption of server operation, be it due to software, hardware, or power failure. This requires that a copy of the user information is held on backup storage (disk). The simplest solution would be to use a file for this purpose. However, this would indeed make protection too vulnerable: files can be accessed easily, and we have refrained from introducing a file protection facility. Instead, the backup of the user information is held on a few permanently reserved sectors on the server machine, which are inaccessible to the file system. ## Administrative Procedures Apart from procedures and variables constituting the queuing mechanism for tasks, the procedures exported from module `Core` all belong to the administration, and they can be divided into two categories: 1. **User Management**: - `UserNo`, `UserNum`, `IncPageCount`, `SetPassword`, `GetUserName`, `GetFileName`. 2. **User Inspection**: - `ListUsers`, `GetUsers`, and `Init` for making changes to the table. The client of the latter category is a module `Users` which is needed by the human administrator of the server facility. ## Conclusion The reader may at this point wonder why a more advanced concept of administration has not been chosen, which would allow the human administrator to operate the server remotely. A quick analysis of the consequences of this widely used approach reveals that a substantial amount of additions to our system would be required. The issue of security and protection would become inflated into dimensions that are hardly justified for our local system. The first consequence would be a differentiation among levels of protection. The administrator would become a so-called super-user with extra privileges, such as changing the user table. And so the game of trying to break the protection measures starts to become an interesting challenge. We have resisted the temptation to introduce additional complexity. Instead, we assume that physical access to the server station is reserved to the administrator. Naturally, module `Users` and in particular the symbol file of `Core` do not belong to the public domain. In concluding, we may point out that the impossibility of activating users’ programs on the server station significantly reduces the possibilities for inflicting damage from the exterior. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 13 Context: # 12 The Compiler ## 12.1 Introduction The compiler is the primary tool of the system builder. It therefore plays a prominent role in the Oberon System, although it is not part of the basic system. Instead, it constitutes a tool module - an application - with a single command: `Compile`. It translates program texts into machine code. Therefore, it is a program inherently machine-dependent; it acts as the interface between source language and target computer. In order to understand the process of compilation, the reader needs to be familiar with the source language Oberon defined in Appendix 1, and with the target computer RISC, defined in Appendix 2. The language is defined as an infinite set of sequences of symbols taken from the language's vocabulary. It is described by a set of equations called syntax. Each equation defines a syntactic construct, or more precisely, the set of sequences of symbols belonging to that construct. It specifies how that construct is composed of other syntactic constructs. The meaning of programs is defined in terms of semantic rules governing each such construct. Compilation of a program text proceeds by analyzing the text and thereby decomposing it recursively into its constructs according to the syntax. When a construct is identified, code is generated according to the semantic rule associated with the construct. The components of the identified construct supply parameters for the generated code. It follows that we distinguish between two kinds of actions: analyzing steps and code generating steps. In a rough approximation we may say that the former are source language dependent and target computer dependent, whereas the latter are source language independent and target computer dependent. Although reality is somewhat more complex, the module structure of this compiler clearly reflects this division. The main module of the compiler is ORP (for Oberon to RISC Parser). It is primarily dedicated to syntactic analysis, parsing. Upon recognition of a syntactic construct, an appropriate procedure is called to the code generator module ORG (for Oberon to RISC Generator). Apart from parsing, ORP checks for type consistency of operands, and it computes the attributes of objects identified in declarations. Whereas ORP mirrors the source language and is independent of a target computer, ORG reflects the target computer, but is independent of the source language. Oberon program texts are regarded as sequences of symbols rather than sequences of characters. Symbols themselves, however, are sequences of characters. We refrain from explaining the reasons for this distinction, but mention that apart from special characters and pairs such as `.`, `,`, as well as identifiers, numbers, and strings are classified as symbols. Furthermore, certain capital letter sequences are symbols, such as `IF`, `END`, etc. Each time the syntax analyzer (parser) proceeds to read the next symbol, it does this by calling procedure `Get`, which constitutes the so-called scanner residing in module ORS (for Oberon to RISC Scanner). It reads from the source text as many characters as are needed to recognize the next symbol. In passing, we note that the scanner also reflects the definition of symbols in terms of characters, whereas the parser is based on the notion of symbols only. The scanner implements the abstraction of symbols. The recognition of symbols within a character sequence is called **lexical analysis**. Ideally, the recognition of any syntactic construct, say `A`, consisting of subconstructs, say `B1`, `B2`, ..., `Bn`, leads to the generation of code that depends only on (1) the semantic rules associated with `A`, and (2) on attributes of `B1`, `B2`, ..., `Bn`. If this condition is satisfied, the construct is said to be context-free, and if all constructs of a language are context-free, then the language is context-free. Syntax and semantics of Oberon adhere to this rule, although with a significant exception. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 14 Context: # Compiler / Parser ORP ## Code generator ORG ### Table handler ORB - Scanner ORS - Texts, Oberon - Files ![Figure 12.1 Compiler's module structure](path/to/image) ## 12.2 Code patterns Before it is possible to understand how code is generated, one needs to know **which** code is generated. In other words, we need to know the goal before we find the way leading to the goal. A fairly concise description of this goal is possible due to the structure of the language. As explained before, semantics are attached to each individual syntactic construct, independent of its context. Therefore, it suffices to list the expected code—instead of an abstract semantic rule—for each syntactic construct. As a prerequisite to understanding the resulting instructions and in particular their parameters, we need to know where declared variables are stored, i.e., which are their addresses. This compiler #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 15 Context: # Variable Allocation in RISC Processors In RISC processors, variable allocation uses a straightforward scheme of sequential allocation of consecutively declared variables. An address is a pair consisting of a base address (in a register) and an offset. Global variables are allocated in the module's data section and the respective base address register is SB (Static Base, see Chapter 6). Local variables are allocated in a procedure activation record on the stack; the respective base register is SP (Stack Pointer). Offsets are positive integers. The amount of storage needed for a variable (called its *size*) is determined by the variable's type. The sizes of basic types are prescribed by the target computer's data representation. The following holds for the RISC processor: | Type | No. of bytes | |--------------------------|--------------| | BYTE, CHAR, BOOLEAN | 1 | | INTEGER, REAL, SET, POINTER, PROCEDURE | 4 | The size of an array is the size of the element type multiplied by the number of elements. The size of a record is the sum of the sizes of its fields. A complication arises due to so-called alignment. By alignment is meant the adjustment of an address to a multiple of the variable's size. Alignment is performed for variable addresses as well as for record field offsets. The motivation for alignment is the avoidance of double memory references for variables being "distributed" over two adjacent words. Proper alignment enhances processing speed quite significantly. Variable allocation using alignment is shown by the example in Fig. 12.2. ## Example of Variable Allocation ``` VAR b0: BYTE; int0: INTEGER; b1: BYTE; int1: INTEGER; ``` ``` b0 int0 | b1 | int1 | -------------- int0 b1 int1 ``` ### Figure 12.2: Alignment of variables We note in passing that a reordering of the four variables lessens the number of unused bytes, as shown in Fig. 12.3. ``` VAR int0, int1: INTEGER; b0, b1: BYTE; ``` ``` int0 int1 b1 | b0 ``` ### Figure 12.3: Improved order of variables Memory instructions compute the address as the sum of a register (base) and an offset constant. Local variables use the *stack pointer* SP (R14) as base, global variables the static base SB (R13). Every module has its own SB value, and therefore access to global (and imported) variables requires two instructions, one for fetching the base value, and one for loading or storing data. If the compiler can determine whether the correct base value has already been loaded into the SB register, the former instruction is omitted. The first 7 sample patterns contain global variables only, and their base SB is assumed to hold the appropriate value. Parameters of branch instructions denote jump distances from the instruction's own location (PC-relative). #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 16 Context: ``` # Pattern 1: Assignment of constants We begin with a simple example of assigning constants to variables. The variables used in this example are global; their base register is SB. Each assignment results in a single instruction. The constant is embedded within the instruction as a literal operand. ## MODULE Pattern1: VAR ch: CHAR; 0 k: INTEGER; 4 x: REAL; 8 s: SET; 12 BEGIN // module entry code ch := '0'; // 40000030 MOV R0 30H k := 10; // 40000000 MOV R0 10 x := 1.0; // 600003F8 MOV R0 3F8000H s := [0, 4, 8]; // 40000111 MOV R0 111H END Pattern1. // module exit code # Pattern 2: Simple expressions The result of an expression containing operators is always stored in a register before it is assigned to a variable or used in another operation. Registers for intermediate results are allocated sequentially in ascending order R0, R1, ..., R11. Integer multiplication and division by powers of 2 are represented by shifts (LSL, ASR). Similarly, the modulus by a power of 2 is obtained by masking off leading bits. The operations of set union, difference, and intersection are represented by logical operations (OR, AND). ## MODULE Pattern2: VAR i, j, k: INTEGER; 0, 4, 8 x, y: REAL; 16, 20 s: SET; 24, 28, 32 BEGIN i := (i + 1) * (i - 1); // LDR R0 S0 // ADD R0 R0 1 // LDR R1 S0 // SUB R1 R1 1 // MUL R0 R0 R1 k := k DIV 17; // LDR R0 S8 // DIV R0 R0 17 // STR R0 S8 k := 8**n; // LDR R0 S12 // LSL R0 R0 3 // STR R0 S8 k := n DIV 2; // LDR R0 S12 // ASR R0 R0 1 // STR R0 S8 k := n MOD 16; // LDR R0 S12 // AND R0 S15 // STR R0 S8 x := -y / (x - 1.0); // LDR R0 S16 // MOV R0 R0 3F80H // FSB R0 R0 R1 // LDR R1 S20 // FDIV R0 R1 R0 s := s + t + u; // FSB R0 R1 0 // STR R0 S16 // LDR R0 S28 // LDR R1 S32 END Pattern2. // module exit code ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 17 Context: # Pattern3: Indexed Variables References to elements of arrays make use of the possibility to add an index value to an offset. The index must be present in a register and multiplied by the size of the array elements. (For integers with size 4 this is done by a shift of 2 bits). Then the index is checked whether it lies within the bounds specified in the array's declaration. This is achieved by a comparison, actually a subtraction, and a subsequent branch instruction causing a trap, if the index is either negative or beyond the upper bound. If the reference is to an element of a multi-dimensional array (matrix), its address computation involves several multiplications and additions. The address of an element A[i1, ..., ik] of a k-dimensional array A with lengths n1, ..., nk is: ``` A(i) + ((...(((i1 * n2) + i2) * n3 + ...) * n_k) + i_k) * n1) + n0 ``` Note that for index checks `CMP` is written instead of `SUB` to mark that the subtraction is merely a comparison, that the result remains unused and only the condition flag registers hold the result. ## MODULE Pattern3: ```assembly VAR i, j, k: INTEGER; 0, 4, 8, 12 a: ARRAY 10 OF INTEGER; 16 x: ARRAY 10, 10 OF INTEGER; 56 y: ARRAY 10, 10 OF INTEGER; 456 BEGIN k := a[j]; LDR R0 S8 CMP R1 10 BLH R12 LSL R0 2 ADD R0 S8 R0 LDR R0 R0 16 STR R0 S8 n := a[5]; LDR R0 S8 STR R0 S8 12 x[i, j] := 2; LDR R0 S8 CMP R1 10 BLH R12 MUL R0 R0 40 ADD R0 R0 S8 CMP R2 R1 10 BLH R12 LDR R0 S8 4 y[i, j] := k - 3; LDR R0 S8 CMP R1 10 BLH R12 MUL R1 R0 400 ADD R0 R0 S8 LDR R1 S8 4 CMP R2 R1 10 END Pattern3. ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 18 Context: # BLH R12 LSL R1 R12 ADO R0 R0 R1 MOV R1 R0 3 STR R1 R0 456 MOV R0 R6 STR R0 SB 1836 END Pattern 3. ## Pattern 4: Record fields and pointers Fields of records are accessed by computing the sum of the record's (base) address and the field's offset. If the record variable is statically declared, the sum is computed by the compiler. ```pascal MODULE Pattern4; TYPE Ptr = POINTER TO Node; Node = RECORD num: INTEGER; 0 name: ARRAY [0..4] OF CHAR; 4 next: Ptr; 12 END; VAR p: Ptr; 12, 16 q: Node; 20 BEGIN r.num := 10; MOV R0 R10 STR R0 SB 20 p.num := 6 LDR R0 SB 12 (p) MOV R1 R0 6 STR R1 R0 0 LDR R0 SB 12 MOV R1 R0 30 STR R1 R0 11 (4+7) p.next := q; LDR R0 SB 12 STR R1 R0 12 p.next.next := NIL LDR R0 SB 12 LDR R0 R0 12 (p.next) MOV R1 R0 0 (NIL) STR R1 R0 12 (p.next.next) END Pattern4. ``` ## Pattern 5: Boolean expressions, IF statements Conditional statements imply that parts of them are skipped. This is done by the use of branch instructions whose operand specifies the distance of the branch. The instructions refer to the condition-register as an implicit operand. Its value is determined by a preceding instruction, typically a compare or a bit-test instruction. The Boolean operators `&` and `OR` are purposely not defined as total functions, but rather by the equations: ``` p & q = if p then q else FALSE p OR q = if p then TRUE else q ``` Consequently, Boolean operators must be translated into branches too. Evidently, branches stemming from if statements and branches stemming from Boolean operators should be merged, if possible. The resulting code therefore does not necessarily mirror the structure of the if statement directly, as can be seen from the code in Pattern5. We must conclude that code generation for Boolean expressions differs in some aspects from that for arithmetic expressions. The example of Pattern 5 is also used to exhibit the code resulting from the standard procedures INC, DEC, INCL, and EXCL. These procedures provide an opportunity to use shorter code in those cases where a single two-operant instruction suffices, i.e., when one of the arguments is identical with the destination. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 19 Context: ``` # MODULE Pattern5; VAR INTEGER: s; SET: 0, 4; BEGIN IF n = 0 THEN LDR R0 S8 ... CMP R0 R0 BNE ... INC(n) LDR R0 S8 ... ADD R0 R0 1 STR R0 S0 END; IF (n >= 0) AND (n < 100) THEN LDR R8 S8 ... LDR R0 S0 (n) CMP R0 R0 BLT 6 LDR R0 S8 CMP R0 R0 100 BGE 3 DEC(n) LDR R0 S8 ... SUB R0 R0 1 STR R0 0 END; IF (ODD(n)) OR (n IN s) THEN LDR S8 R0 ... LDR R0 S0 (n) AND R0 R0 BNE 5 LDR R0 S8 4 (s) LDR R1 S8 0 ADD R1 R1 1 ROL R0 R1 BPL 2 STR R0 S0 n = -1000 MOV R0 R0 -1000 STR R0 S0 END; IF n < 0 THEN LDR R0 S8 ... LDR R0 S0 CMP R0 R0 BGE 3 MOV R0 R0 {} STR R0 S8 4 B 17 END; ELSIF n < 10 THEN LDR R0 S8 ... LDR R0 S0 CMP R0 R0 10 BGE 3 MOV R0 R1 1 STR R0 S8 4 B 18 END; ELSIF n < 100 THEN LDR R0 S8 ... LDR R0 S0 CMP R0 R0 100 BGE 3 MOV R0 R0 2 STR R0 S8 4 B 19 ELSE MOV R0 R4 ... LDR R0 S8 ... STR R0 S8 4 END; END Pattern5. # Pattern 6: While and repeat statements. ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 20 Context: ``` MODULE Pattern6; VAR i: INTEGER; BEGIN i := 0; WHILE i < 10 DO LDR R8 0 LDR R8 0 CMP R0 10 BGE 4 LDR R8 0 ADD R0 R2 STR R8 0 END; REPEAT i := i - 1 LDRB R8 0 LDR R0 R5 SUB R0 R1 STR R0 0 UNTIL i = 0 CMP R0 0 BNE -7 END Pattern6; Pattern 7: For statements. MODULE Pattern7; VAR i, m, n: INTEGER; BEGIN FOR i := 0 TO n - 1 DO LDR R1 S1 SUB R1 R1 CMP LNK R0 R1 BGT 7 STR R0 S8 4 LSL R0 R1 STR R0 S4 LDR R8 0 ADD R0 R1 END; B -11 END Pattern7; Pattern 8: Proper procedures. MODULE Pattern8; VAR z: INTEGER; PROCEDURE P(x: INTEGER; VAR y: INTEGER); BEGIN SUB SP SP 16 // adjust SP STR LNK SP 0 // push ret addr STR R0 SP 4 // push x STR R1 SP 8 // push @y z := x; LDR R0 SP 4 // x STR R0 SP 12 // z y := z; LDR R0 SP 12 // z END Pattern8; ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 21 Context: # Pattern 9: Function procedures They are handled in exactly the same manner as proper procedures, except that a result is returned in register R0. If the function is called in an expression at a place where intermediate results are held in registers, these values are put onto the stack before the call, and they are restored after return (not shown here). ## MODULE Pattern9; ### VAR x: REAL; ```assembly PROCEDURE F(x: REAL); REAL; BEGIN SUB SP, SP, 8 STR LINK SP, 0 ; push ret adr STR R0 SP, 4 PUSH x IF x >= 1.0 THEN LDR R0 SP, 4 MOV R1, R0, 0xBF80H FSB R0, R0, R1 BLT 4 x := F(F(x)); LDR R0 SP, 4 BL -9 BL -10 STR R0 SP, 4 END; RETURN x; END F; ``` END Pattern9. # Pattern 10: Dynamic array parameters Dynamic array parameters are passed by loading a descriptor on the stack, regardless of whether they are value- or VAR- parameters. The descriptor consists of the actual variable's address and the array's length. (Only one-dimensional dynamic arrays are handled.) ## MODULE Pattern10; ### VAR A: ARRAY [0..12] OF INTEGER; ```assembly PROCEDURE P(var: ARRAY OF INTEGER); VAR n, i: INTEGER; BEGIN SUB SP, SP, 20 STR LINK SP, 0 STR R0 SP, 4 ; x STR SP, SP, 8.len LDR R0 SP, 12 LDR R1 SP, 8.len CMP R0, R1 BHI R12 LSL R0, R0, 2 LDR R1 SP, 4 ; x END; ``` END Pattern10. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 22 Context: ```markdown # Code Patterns ## Pattern 11: Sets This code pattern exhibits the construction of sets. If the specified elements are constants, the set value is computed by the compiler. Otherwise, sequences of move and shift instructions are used. Since shift instructions do not check whether the shift count is within sensible bounds, the results are unpredictable if elements outside the range 0..31 are involved. ```assembly MODULE Pattern11; VAR S: SET; m, n: INTEGER; BEGIN s := (m); LDR R0 SB 4 ; m MOV R1 R0 LSL R0 R1 0 STR R0 SB 0 s := (0..n); LDR R0 SB 8 ; n MOV R1 R0 -2 LSL R0 R1 0 XOR R0 R1 0 STR R0 SB 1 s := (m..31); LDR R0 SB 4 ; m MOV R1 R0 31 MOV R1 R0 LSL R1 R1 0 STR R0 SB 0 XOR R0 R1 0 s := (m..n); LDR R0 SB 4 ; m LDR R1 SB 8 ; n MOV R2 R0 -2 LSL R2 R1 2 LSR R0 R1 1 LSL R0 R1 1 XOR R0 R1 0 STR R0 SB 0 IF n IN (2, 3, 5, 7, 11, 13) THEN MOV R0 R0 28H LDR R1 SB 8 ADD R1 R1 1 ASP R0 R1 BPL 2 END Pattern11; ``` ## END P; ADD R0 R1 R0 LDR R0 R0 0 STR R0 SP 16 LDR R0 SP 12 ; i ADD R0 R1 LDR R1 SP 8 ; x.len CMP R2 R0 R1 BLHI R12 LSL R0 R2 0 LDR R1 SP 4 ; x ADD R0 R1 R0 ADD R1 R1 5 STR R1 R0 0 ADD SP SP 20 B R15 END P; ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 23 Context: ```markdown m := 1 MOV R0 R0 1 STR R0 S8 4 m END END Pattern11. # Pattern 12: Imported variables and procedures When a procedure is imported from another module, its address is unavailable to the compiler. Instead, the procedure is identified by a number obtained from the imported module's symbol file. In place of the offset, the branch instruction holds (1) the number of the imported module, (2) the number of the imported procedure, and (3) a link in the list of BL instructions calling an external procedure. This list is traversed by the linking loader, that computes the actual offset (fixup, see Chapter 6). Imported variables are also referenced by a variable's number. In general, an access required two instructions. The first loads the static base register SB from a global table with the address of that module's data section. The module number of the imported variable serves as an index. The second instruction loads the address of the variable, using the actual offset fixed up by the loader. In the following example, modules Pattern12a and Pattern12b both export a procedure and a variable. They are referenced from the importing module Pattern12c. ## MODULE Pattern12a; VAR k: INTEGER; PROCEDURE P*; BEGIN k := 1 END P; END Pattern12a. ## MODULE Pattern12b; VAR x: REAL; PROCEDURE Q*; BEGIN x := -1 END Q; END Pattern12b. ## MODULE Pattern12c; IMPORT Pattern12a, Pattern12b; VAR i: INTEGER; y: REAL; BEGIN i := Pattern12a.k; y := Pattern12b.x; END Pattern12c. # Pattern 13: Record extensions with pointers Fields of a record type R1, which is declared as an extension of a type R0, are simply appended to the fields of R0, i.e., their offsets are greater than those of the fields of R0. When a record is statically declared, its type is known by the compiler. If the record is referenced via a pointer, however, this is not the case. A pointer bound to a base type R0 may well refer to a record of an extension R1 of R0. Type tests (and type guards) allow to test for the actual type. This requires that a type can be identified at the time of program execution. Because the language defines name equivalence instead of structural equivalence of types, a type may be identified by a number. We use the address of a unique type descriptor for this purpose. ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 24 Context: Therefore, type tests consist of a simple address comparison which is very fast. Type descriptors are stored in the module's area for data. Their address is called `type tag`. The tag of a (dynamically allocated) variable is stored as a prefix to its record (with offset -8). A type descriptor contains - in addition to information stored for use by the garbage collector - a table of tags of all base types. If, for instance, a type `R2` is an extension of `R1` which is an extension of `R0`, the descriptor of `R2` contains the tags of `R1` and `R0` as shown in Fig. 12.4. The table has a fixed number of 3 entries. ![Figure 12.4 Type descriptors](#) A type test of the form `p IS T` then consists of a comparison of the type tag of `p` at address `p-8` with the tag held in the descriptor of `T` at the extension level of the type of `p`. A type guard `p(T)` is synonymous to the statement: ``` IF ~ (p IS T) THEN abort END ``` The following example features 3 record types with associated pointer types, and hence also 3 type descriptors. Each descriptor is 5 words long. Their addresses, and therefore their tags, are 0, 20, and 40 respectively. ``` 20 00000020 00014006 FFFFFFFF FFFFFFFF FFFFFFFF 40 00000020 00014005 00028001 FFFFFFFF FFFFFFFF ``` ### MODULE Pattern13; ``` PO = POINTER TO R0; P1 = POINTER TO R1; P2 = POINTER TO R2; R0 = RECORD R0 : INTEGER END; R1 = RECORD R1 : INTEGER END; R2 = RECORD R2 : INTEGER END; VAR p0: P0; p1: P1; p2: P2; BEGIN p0.x := 0; p1.y := 1; p(P0).y := 3; LDR R0 S0 60 ; p0 LDR R1 S0 60 ; tag(p0) LDR R1 R1 4 ADD R2 S2 20 ; TD P1 CMP R2 R2 R1 ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 25 Context: # Pattern 14: Record extensions as VAR parameters Records occurring as VAR-parameters may also require a type test at program execution time. This is because VAR-parameters effectively constitute hidden pointers. Type tests and type guards on VAR-parameters are handled in the same way as for variables referenced via pointers, with a slight difference, however. Statically declared record variables may be used as actual parameters, and they are not prefixed by a type tag. Therefore, the tag has to be supplied together with the variable's address when the procedure is called, i.e., when the actual parameter is established. Record structured VAR-parameters therefore consist of address and type tag. This is similar to dynamic array descriptors consisting of address and length. ```assembly 00000020 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000020 00014006 FFFFFFFF FFFFFFFF FFFFFFFF ``` ## MODULE Pattern14; ### TYPE R0 = RECORD (a, b, c: INTEGER) ; VAR r0: R0; r1: R1; ### PROCEDURE P(VAR r: R0); BEGIN r.a := 1; LDR R1 SP 4 ; STR R0 R1 0 ; r(r1).d := 2; LDR R0 R8 tag(r) ; ADD R1 R8 R1 ; CMP R1 R12 R1 ; BLNE R12 ; MOV R0 R0 2 ; LDR R1 SP 4 ; STR R0 R1 12 ; END P; ... BEGIN P(r0); ADD R0 SB 40 r0 ; ADD R1 SB 0 tag(R0) ; BL P END; #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 26 Context: ```markdown # Pattern 15: Array assignments and strings. ## MODULE Pattern15: ### VAR s0: ARRAY 32 OF CHAR; ### PROCEDURE P(k: ARRAY OF CHAR); END P; BEGIN s0 := "ABCDEF"; ADD R0, SB, 0 @s0 ADD R1, SB, 64 @"ABCDEF" LDR R2, R1, 0 ADD R1, R1, 4 STR R2, R0, 0 ADD R0, 0 ASR R2, R2, 24 ; test for 0X BNE = s0 := s1; ADD R0, SB, 0 @s0 ADD R1, SB, 32 @s1 MOV R2, R0, 8 len LDR R3, R1, 0 ADD R1, R1, 4 STR R3, R0, 0 ADD R0, 4 SUB R2, R2, 1 BNE = P(1); ADD R0, SB, 32 @s1 MOV R1, 0, 32 len BL -38 P P("012345"); ADD R0, SB, 72 @"012345" MOV R1, 0, 7 ; len (incl 0X) BL -42 P P("%"); ADD R0, SB, 80 @"%" MOV R1, 0, 2 len BL -46 P END Pattern15. # Pattern 16: Predeclared procedures. ## MODULE Pattern16: ### VAR m: INTEGER; a: REAL; s: ARRAY 10 OF INTEGER; b: ARRAY 16 OF CHAR; BEGIN INC(m); LDR R1, R0, 0 @m ADD R1, R1, 1 STR R1, R0, 0 DEC(m, 10); ADD R0, SB, 4 @n LDR R1, R0, 0 SUB R1, R1, 10 STR R1, R0, 0 INCL(u, 3); ADD R0, SB, 12 @u LDR R1, R0, 0 OR R1, R1, 8 STR R1, R0, 0 {3} ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 27 Context: ``` EXCL(u, 7); ADD R0 SB 12 @u LDR R1 R0 AND R1 R1 -129 ;[-7] STR R1 R0 ASSERT(m < n); LDR R1 SB 8 CMP R0 R1 BLGE R12 UNPK(x, n); LDR R0 SB 8 x ASR R1 R0 23 SUB R1 R1 127 STR R1 SB 44 n LSL R1 R1 23 STR R0 R0 R1 STR R0 SB 8 x LDR R0 SB 8 LDR R1 SB 4 n LSL R1 R1 23 ADD R0 R0 R1 STR R0 SB 8 x s := '0123456789'; ADD R1 SB 96 @s ADD R1 SB 128 adr of string LDB R2 R1 0 ADD R1 R1 4 STRB R2 R0 0 ADD R0 R0 4 ASR R2 R2 24 BNE = 0 BNE = IF s < t THEN ADD R0 SB 96 @s ADD R1 SB 112 @t LDB R2 R0 0 ADD R0 R1 1 LDB R3 R1 0 ADD R1 R1 1 CMP R4 R2 R3 BNE = 2 CMP R4 R2 0 BNE = BGE = 3 m := -1; MOV R0 R1 STR R0 SB 0 m END END Pattern16. Pattern 17: Predefined functions. MODULE Pattern17; VAR m: INTEGER; x, y: REAL; b: BOOLEAN; ch: CHAR; BEGIN n := ABS(m); LDR R0 SB 0 m CMP R0 R0 BGE 2 MOV R1 R0 SUB R0 R1 R0 STR R0 SB 8 n LSL R0 R0 1 x y := ABS(x); LDR R0 SB 8 x LSL R0 R0 1 ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 28 Context: ``` ROR R0, R1 STR R0, SB 12 y b := ODD(n); LDR R0, SB 4 n AND R0, R1 BEQ 2 MOV R0, R1 B 1 MOV R0, 0 STS R0, SB 16 b n := ORD(ch); LDB R0, SB 17 ch STR R0, SB 4 n n := FLOOR(x); LDR R0, SB 4 x MOV R1, R0, 480H FAD R0, R1 STR R0, SB 1 floor STR R0, SB 4 n y := FLT(m); LDR R0, SB 0 m MOV R1, R0, 480H FAD R0, R1 float STR R0, SB 12 y n := LSL(m, 3); LSL R0, SB 4 n STR R0, SB 4 n n := ASR(m, 8); LDR R0, SB 0 8 ASR R0, SB 4 STR R0, SB 4 n m := ROR(m, n); LDR R1, SB 4 LDR R0, R0, R1 ROR R0, R1 STR R0, SB 0 END Pattern17 # 12.3. Internal data structures and module interfaces ## 12.3.1. Data structures In Section 12.1 it was explained that declarations inherently constitute context-dependence of the translation process. Although parsing still proceeds on the basis of a context-free syntax and relies on contextual information only in a few isolated instances, information provided by declarations affects the generated code significantly. During the processing of declarations, their information is transferred into the “symbol table”, a data structure of considerable complexity, from where it is retrieved for the generation of code. This dynamic data structure is defined in module ORB in terms of two record types called `Object` and `Struct`. These types prevail over all modules with the exception of the scanner. They are therefore explained before further details of the compiler are discussed (see module ORB below). For each declared identifier an instance of type `Object` is generated. The record holds the identifier and the properties associated with the identifier given in its declaration. Since Oberon is a statically typed language, every object has a type. It is represented in the record by its type field, which is a pointer to a record of type `Struct`. Since many objects may be of the same type, it is appropriate to record the type’s attributes only once and to refer to them via a pointer. The properties of type `Struct` will be discussed below. The kind of object which a table entry represents is indicated by the field class. Its values are denoted by declared integer constants: `Var` indicates that the entry describes a variable, `Con` a constant, `Fct` a record field, `Par` a VAR-parameter, and `Proc` a procedure. Different kinds of entries carry different attributes. A variable or a parameter carries an address, a constant has a value, a record field has an offset, and a procedure has an entry address, a list of parameters, and a result type. For each class the introduction of an extended record type would seem advisable. This was not done, however, for three reasons. First, the compiler was first formulated in (a subset of) 28 ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 29 Context: Module-2 which does not feature type extension. Second, not making use of type extensions would make it simpler to translate the compiler into other languages for porting the language to other computers. And third, all extensions were known at the time the compiler was planned. Hence, extensibility provided no argument for the introduction of a considerable variety of types. The simplest solution lies in using the multi-purpose fields for and disc for class-specific attributes. For example, val holds an address for variables, parameters, and procedures, an offset for record fields, and a value for constants. The definition of a type yields a record of type `Struct`, regardless of whether it occurs within a type declaration, in which case also a record of type `Object` (class = `Typ`) is generated, or in a variable declaration, in which case the type remains anonymous. All types are characterized by a form and a size. A type is either a basic type or a constructed type. In the latter case, it refers to one or more other types. Constructed types are arrays, records, pointers, and procedural types. The attribute `form` refers to this classification. Its value is an integer. Just as different object classes are characterized by different attributes, different forms have different attributes. Again, the introduction of extensions of type `Struct` was avoided. Instead, some of the fields of type `Struct` remain unused in some cases, such as for basic types, and others are used for form-specific attributes. For example, the attribute `base` refers to the element type in the case of an array, to the result type in the case of a procedural type, to the type to which a pointer is bound, or to the base type of a (extended) record type. The attribute `disc` refers to the parameter list in the case of a procedural type, or to the list of fields in the case of a record type. As an example, consider the following declarations. The corresponding data structure is shown in Fig. 12.5. For details, the reader is referred to the program listing of module `ORB` and the respective explanations. ``` CONST N = 100; TYPE Ptr = POINTER TO Rec; Rec = RECORD n: INTEGER; p, q: Ptr; END; VAR k: INTEGER; a: ARRAY [0..N-1] OF INTEGER; PROCEDURE P(x: INTEGER): INTEGER; ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 30 Context: # Figure 12.5. Representation of declarations | Object | Type | |-----------|------------------| | name | class | | val | type | | next | desc | | | nofpa | | | len | | N | Con | |----------|-----| | 100 | | | Ptr | Typ | |-----|---------| | | Pointer | 4 | | Rec | Typ | | | Record | 12 | | | NIL | | 0 | n | Fid | |----|----|-----| | 0 | p | Fid | | 4 | q | Fid | | 8 | | | k | Var | |----|------------| | 0 | a | Var | | 4 | Array | 400 | | NIL| | | 100| x | Var | | 4 | NIL | | P | Con | |----|-----------| | 4 | Proc | | NIL| | | 1 | | | intType | Int | 4 | Only entries representing constructed types are generated during compilation. An entry for each basic type is established by the compiler's initialization. It consists of an Object holding the standard type's identifier and a Struct indicating its form, denoted by one of the values: Byte, Bool, Char, Int, Real, or Set. The object records of the basic types are anchored in global pointer variables in module ORB (which actually should be regarded as constants). Not only are entries created upon initialization for basic types, but also for all standard procedures. Therefore, every compilation starts out with a symbol table reflecting all standard, pervasive identifiers and the objects they stand for. We now return to the subject of Objects. Whereas objects of basic classes (Const, Var, Par, Fld, Typ, SProc, SFunc, and Mod) directly reflect declared identifiers and constitute the context in which statements and expressions are compiled, compilations of expressions typically generate. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 31 Context: # Anonymous Entities of Additional, Non-Basic Modes Such entities reflect selectors, factors, terms, etc., i.e., constituents of expressions and statements. As such, they are of a transitory nature and hence are not represented by records allocated on the heap. Instead, they are represented by record variables local to the processing procedures and are therefore allocated on the stack. Their type is called `Item` and is a slight variation of the type `Object`. Items are not referenced via pointers. Let us assume, for instance, that a term `x*y` is parsed. This implies that the operator and both factors have been parsed already. The factors `x` and `y` are represented by two variables of type `Item of Var` mode. The resulting term is again described by an item, and since the product is transitory, i.e., has significance only within the expression of which the term is a constituent, it is to be held in a temporary location, in a register. In order to express that an item is located in a register, a new, non-basic mode `Reg` is introduced. Effectively, all non-basic modes reflect the target computer's architecture, in particular its addressing modes. The more addressing modes a computer offers, the more item modes are needed to represent them. The additional item modes required by the RISC processor are declared in module `ORG`: | Reg | direct register mode | |------|-----------------------| | Reg1 | indirect register mode | | Cond | condition code mode | The use of the types `Object`, `Item`, and `Struct` for the various modes and forms, and the meaning of their attributes are explained in the following tables: ## Objects: | class | val | a | b | r | |-------|-----|-----|-----|-----| | 0 | Undef | | | | | 1 | Const | val | | | | 2 | Var | adr | base | | | 3 | Par | adr | off | | | 4 | Fld | off | off | | | 5 | Typo | TAdr | TAdr | modno | | 6 | SProc | num | | | | 7 | SFunc | num | | | | 8 | Mod | | | | ## Items: | form | nopar | len | desc | base | |------|-------|-----|------|--------| | 7 | Pointer | | base type | | 10 | ProcTyp | nopar | result type | | 12 | Array | nofel | element type | | 13 | Record | ext | lev | fields extension type | Items have an attribute called `lev` which is part of the address of the item. Positive values denote the level of nesting of the procedure in which the item is declared; `lev = 0` implies a global object. Negative values indicate that the object is imported from the module with number `-lev`. The three types `Object`, `Item`, and `Struct` are defined in module `ORB`, which also contains procedures for accessing the symbol table. ## 12.3.2. Module Interfaces (Ensure any additional information present in the reference image is filled here.) #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 32 Context: Before embarking on a presentation of the compiler's main module, the parser, an overview of its remaining modules is given in the form of their interfaces. The reader is invited to refer to them when studying the parser. The interface of the scanner module `ORS` is simple. It defines the numeric values of all symbols, but its chief constituent is procedure `Get`. Each call yields the next symbol from the source text, identified by an integer. Global variables represent attributes of the read symbol in certain cases. If a number was read, `val` or `nal` hold its numeric value. If an identifier or a string was read, `str` holds the ASCII values of the characters read. Procedure `Mark` serves to generate a diagnostic output indicating a brief diagnostic and the scanner's current position in the source text. This procedure is located in the scanner because only the scanner has access to its current position. `Mark` is called from all other modules. ```pascal IMPORT ORS; (* "Scanner" *) TYPE Ident = ARRAY 32 OF CHAR; VAR len: INTEGER; rval: REAL; id: Ident; str: ARRAY 256 OF CHAR; errn: BOOLEAN; PROCEDURE Mark(msg: ARRAY OF CHAR); PROCEDURE Get(VAR sym: INTEGER); PROCEDURE Init(source: Texts.Text; pos: INTEGER); END ORS. ``` Module `ORB` defines the basic data structures representing declared objects and their types. It also contains procedures for accessing these structures. `NewObj` serves to insert a new identifier, and it returns a pointer to the allocated object. `ThisObj` returns the pointer to the object whose name equals the global scanner variable `ORS.id`. `ThisImport` and `ThisField` deliver imported objects and record fields with names equal to `ORS.id`. Procedure `Import` serves to read the specified symbol file and to enter its identifier in the symbol table (`class = Mod`). Finally, `Export` generates the symbol file of the compiled module, containing descriptions of all objects and structures marked for export. ```pascal DEFINITION ORB; (* "Base table handler" *) TYPE Object = POINTER TO ObjDesc; Type = POINTER TO TypeDesc; ObjDesc = RECORD class, len, expn: INTEGER; expd: BOOLEAN; next: Object; type: Type; name: ORS.Id; val: INTEGER; END; TypeDesc = RECORD form: ref, mod: INTEGER; (* 'ref is used for import/export only' *) nofpnt: INTEGER; (* 'for records: extension level' *) len: INTEGER; (* 'for records: address of descriptor' *) desc: TypeObj; base: Type; size: INTEGER; END; VAR TopScope: Object; byteType, boolType, charType, intType, realType, setType, nilType, noType, strType: Type; ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 33 Context: ``` # PROCEDURE Init; # PROCEDURE Close; # PROCEDURE NewObj (VAR obj: Object; id: ORS.Ident; class: INTEGER); # PROCEDURE ThisObj(): Object; # PROCEDURE ThinObject (mod: Object): Object; # PROCEDURE Finished (rec: Type): Object; # PROCEDURE OpenScope; # PROCEDURE CloseScope; # PROCEDURE Import (VAR mod: modid: ORS.Ident); # PROCEDURE Export (VAR mod: ORS.Ident); # END ORG. VAR newSF: BOOLEAN; VAR key: INTEGER; Module ORG contains the procedures for code generation. The names of these procedures indicate the respective constructs for which code is to be produced. Note that an individual code generator procedure is provided for every standard, predefined procedure. This is necessary because they generate in-line code. ## DEFINITON ORG; CONST WordSize = 4; TYPE Item = RECORD mode: INTEGER; typ: ORB.Type; a, b: INTEGER; rdb: BOOLEAN; (* "read only" *) END; VAR x: INTEGER; ### PROCEDURES - PROCEDURE MakeConstItem(VAR x: Item; typ: ORB.Type; var: INTEGER); - PROCEDURE MakeRealItem(VAR x: Item; val: REAL); - PROCEDURE MakeStringItem(VAR x: Item; len: INTEGER); - PROCEDURE MakeWrite(VAR x: Item; var: ORB.Object; curlev: INTEGER); - PROCEDURE Field(VAR x: Item; y: Object); (* x <- x.y *) - PROCEDURE Index(VAR x: VAR; y: Item); (* x <- x[y] *) - PROCEDURE Ref(VAR x: Item); - PROCEDURE BufPut(VAR x: Item; VAR y: INTEGER); - PROCEDURE TypeTest(VAR x: Item; t: ORB.Type; vararg: BOOLEAN); - PROCEDURE Not(VAR x: Item); (* x <- ~x, Boolean operators *) - PROCEDURE And1(VAR x: Item); (* x <- x && x *) - PROCEDURE And(VAR x: Item); (* x <- x && x OR *) - PROCEDURE Or1(VAR x: Item); (* x <- x OR x *) - PROCEDURE Or2(VAR x: VAR; y: Item); - PROCEDURE Neg(VAR x: Item); (* x <- -x, arithmetic operators *) - PROCEDURE AddOp(VAR x: Item); (* x <- x + y *) - PROCEDURE SubOp(VAR x: VAR; y: Item); (* x <- x - y *) - PROCEDURE MulOp(VAR x: Item; y: Item); (* x <- x * y *) - PROCEDURE DivOp(VAR x: INTEGER; VAR y: Item); (* x <- x / y *) - PROCEDURE RealOp(VAR x: INTEGER; VAR y: Item); (* x <- x op y *) - PROCEDURE Singleton(VAR x: Item); (* x <- x, set operators *) - PROCEDURE Set(VAR x: VAR; y: Item); (* x <- x ∪ y *) - PROCEDURE Insert(VAR x: Item; y: Item); (* x <- x + y *) - PROCEDURE SetOp(VAR op: INTEGER; VAR x: VAR; y: Item); (* x <- x op y *) - PROCEDURE IntRelation(VAR op: INTEGER; VAR x: Item); (* x <- x op y *) - PROCEDURE RealRelation(op: INTEGER; VAR y: Item); (* x <- x op y *) - PROCEDURE StringRelation(VAR x: VAR; y: Item); (* x <- x y *) - PROCEDURE StrToChar(VAR x: Item); (* "assignments" *) - PROCEDURE Store(VAR x: VAR; y: Item); (* x <- y *) - PROCEDURE StoreStr(VAR x: Item; y: Item); (* x <- y *) - PROCEDURE CopyString(VAR x: VAR; y: Item); (* from x to y *) ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 34 Context: ``` PROCEDURE VarParam(VAR x: item; ftype: ORB.Type); (* parameters *) PROCEDURE ValueParam(VAR x: item); PROCEDURE OpenParam(VAR x: item); PROCEDURE StringParam(VAR x: item); PROCEDURE For(VAR x, y: item; VAR l: LONGINT); (* For Statements *) PROCEDURE For2(VAR x, y: item); PROCEDURE Here() : LONGINT; PROCEDURE Flump(VAR l: LONGINT); PROCEDURE CJump(VAR x: item); PROCEDURE BJump(VAR l: LONGINT); PROCEDURE CJump(VAR x: item; l: LONGINT); PROCEDURE RJump(VAR x: item); PROCEDURE Proc(VAR x: item; VAR l: LONGINT); PROCEDURE Call(VAR x: item; l: LONGINT); PROCEDURE Enter(paraId: LONGINT; locTable: LONGINT; int: BOOLEAN); PROCEDURE Return(form: INTEGER; VAR x: item; size: LONGINT; int: BOOLEAN); (* inline code procedures *) PROCEDURE Increment(updown: LONGINT; VAR x, y: item); PROCEDURE Include(index: LONGINT; VAR x, y: item); PROCEDURE Assert(VAR x: item); PROCEDURE New(VAR x: item); PROCEDURE Pack(VAR x: item); PROCEDURE Length(VAR x: y: item); PROCEDURE Get(VAR x: item); PROCEDURE Put(VAR x, y: item); PROCEDURE Copy(VAR x, y: item); PROCEDURE LDSPR(VAR x: item); PROCEDURE LRDP(VAR x: item); PROCEDURE LRED(VAR x: item); (* inline code functions *) PROCEDURE Abs(VAR x: item); PROCEDURE Sqrt(VAR x: item); PROCEDURE DStr(VAR x: item); PROCEDURE Floor(VAR x: item); PROCEDURE Float(VAR x: item); PROCEDURE Or(VAR x: item); PROCEDURE In(VAR x: item); PROCEDURE Shift(VAR LONGINT; VAR x, y: item); PROCEDURE ABC(VAR x: item); PROCEDURE SLC(VAR x: item); PROCEDURE UML(VAR x, y: item); PROCEDURE BVAR(VAR x: item); PROCEDURE Register(VAR x: item); PROCEDURE HVAR(VAR x: item); PROCEDURE Add(VAR x: item); PROCEDURE Open(n: INTEGER); PROCEDURE SetDataSize(id: LONGINT); PROCEDURE Header(); PROCEDURE Close(VAR mod: ORS.Ident; key, n: LONGINT); END ORG. # 12. A Parser The main module ORP constitutes the parser. Its single command **Compile** - at the end of the program listing - identifies the source text according to the Oberon command conventions. It then calls procedure **Module** with the identified source text as parameter. The command forms are: ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 35 Context: # ORP Compiler ## Overview ### ORP.Compile @ The most recent selection identifies the beginning of the source text. ### ORP.Compile ^ The most recent selection identifies the name of the source file. ### ORP.Compile f0 | f1 | ... These are the names of source files. File names and the characters `@` and `^` may be followed by an option specification `/s`. Option `/s` enables the compiler to overwrite an existing symbol file, thereby invalidating clients. ## Parser Design The parser is designed according to the proven method of top-down, recursive descent parsing with a look-ahead of a single symbol. The last symbol read is represented by the global variable `sym`. Syntactic entities are mirrored by procedures of the same name. Their goal is to recognize the specified construct in the source text. The start symbol and corresponding procedure is `Module`. The principal parser procedures are shown in Fig. 12.6, which also exhibits their calling hierarchy. Loops in the diagram indicate recursion in the syntactic definition. ### Figure 12.6: Parser Procedure Hierarchy ``` Module ___________ | | Declarations StatSeq | | Type ProcDecl _______ _______ | | | | RecType ArrayType FPSection | | | FormalType ParameterList ________ | | ParamList SimpleExp ____________ | | term expression | factor | element ``` ## Rule Violations The rule of parsing strictly based on a single-symbol look-ahead and without reference to context is violated in three places. The prominent violation occurs in statements. If the first symbol of a statement is an identifier, the decision of whether an assignment or a procedure call is to be recognized is based on contextual information, namely the class of the identified object. The second violation occurs in `qualified`: if the identifier `x` preceding a period denotes a module, it is recognized together with the subsequent identifier as a qualified identifier. Otherwise, it is supposedly denotes a record variable. The third violation is made in procedure `selector`: if an identifier is followed by a left parenthesis, the decision of whether a procedure call or a type guard is to be recognized is again made on the basis of contextual information, namely the mode of the identified object. ## Error Discovery A fairly large part of the program is devoted to the discovery of errors. Not only should they be properly diagnosed, but a much more difficult requirement is that the parsing process should continue on the basis of a good guess about the structure that the text should most likely have. The parsing process must continue with some assumption and possibly after skipping a short piece of the object. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 36 Context: # Source Text Hence, this aspect of the parser is mostly based on heuristics. Incorrect assumptions about the nature of a syntactic error lead to secondary error diagnostics. There is no way to avoid them. A reasonably good result is obtained by the fact that procedure `ORS.Mark` initiates an error report, if it is less than 10 characters ahead of the last one. Also, the language Oberon is designed with the property that most large constructs begin with a unique symbol, such as IF, WHILE, CASE, RECORD, etc. These symbols facilitate the recovery of the parsing process in the erroneous text. More problematic are open constructs which neither begin nor end with key symbols, such as types, factors, and expressions. Relying on heuristics, the source text is skipped up to the first occurrence of a symbol which may begin a construct that follows the one being parsed. The employed scheme may not be the best possible, but it yields quite acceptable results and keeps the amount of program devoted to the handling of erroneous texts within justifiable bounds. ## Type Checking Besides the parsing of text, the Parser also performs the checking for type consistency of objects. This is based on type information held in the global table, gained during the processing of declarations, which is also handled by the routines which parse. Thereby an unjustifiably large number of very short procedures is avoided. However, the strict target-computer independence of the parser is violated slightly: Information about variable allocation strategy including alignment, and about the sizes of basic types is used in the parser module. Whereas the former violation is harmless, because the allocation strategy is hardly controversial, the latter case constitutes a genuine target-dependence embodied in a number of explicitly declared constants. Mostly these constants are contained in the respective type definitions, represented by records of type `Typ` initialized by `ORB`. The following procedures allocate objects and generate elements of the symbol table: | Declarations | Object (Con), Object (Typ), Object (Var) | |---------------------|-------------------------------------------| | ProcedureDeclaration | Object (Proc) | | FormalType | Object (Var), Object (Par) | | ORB.Import | Object (Mod) | | RecordType | Object (Fid), Type (Record) | | ArrayType | Type (Array) | | ProcedureType | Type (ProcTyp) | | Type | Type (Pointer) | | FormalType | Type (Array) | ## Forward Declarations An inherently nasty subject is the treatment of forward references in a single-pass compiler. In Oberon, there are two such cases: 1. Forward declarations of procedures. They have been eliminated from the revision of the Oberon language in the year 2007 as they should be avoided if ever possible. If it is impossible, a remedy is to declare a variable of the given procedure type, and assign the procedure to be forwarded to this variable. The nastiness of procedure forward declarations originates in the necessity to specify parameters and result type in the forward declaration. These must be repeated in the actual procedure declaration, and one expects that a compiler verifies the equality (or equivalence) of the two declarations. This is a heavy burden for a case that very rarely occurs. 2. Forward declarations of pointer types also constitutes a nasty exception, but its exclusion would be difficult to justify. If in a pointer declaration the base type (to which the pointer is bound) is not found in the symbol table, a forward reference is therefore automatically assumed. An entry for the pointer type is generated anyway (see procedure Type) and an element is inserted in the list of pointer types to be fixed up. This list is headed by the global variable `pobsList`. When later in the text a declaration of a record type is encountered with the same identifier, the forward entry is recognized and the proper link is established (see procedure Declarations). The compiler must check for undefined forward references when the current declaration scope is closed. This check is performed at the end of procedure Declarations. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 37 Context: The IF statement had been eliminated from the language in its revision of 2007. Here it reappears in the form of a case statement, whose cases are not labelled by integers, but rather by types. What formerly was written as: ```markdown IF x IS T1 THEN WITH x:T1 DO ... x ... END ELSEIF x IS T2 THEN WITH x:T2 DO ... x ... END ELSEIF ... END ``` is now written more simply and more efficiently as: ```markdown CASE x OF T1: ... x ... | T2: ... x ... | ... END ``` where T1 and T2 are extensions of the type T0 of the case variable x. Compilation of this form of case statement merges the regional type guard of the former with statements with the type test of the former if statements. This case statement represents the only case where a symbol table entry - the type of x - is modified during compilation of statements. When the end of the with statement is reached, the change must be reverted. ## 12.5 The scanner The scanner module ORS embodies the lexicographic definitions of the language, i.e., the definition of abstract symbols in terms of characters. The scanner's substance is procedure `Get`, which scans the source text and, for each call, identifies the next symbol and yields the corresponding integer code. It is most important that this process be as efficient as possible. Procedure `Get` recognizes letters indicating the presence of an identifier (or reserved word), and digits signifying the presence of a number. Also, the scanner recognizes comments and skips them. The global variable `ch` stands for the last character read. A sequence of letters and digits may either denote an identifier or a key word. In order to determine this, a search is made in a table containing all key words for each would-be identifier. This table is sorted alphabetically and according to the length of reserved words. It is initialized when the compiler is loaded. The presence of a digit signals a number. Procedure `Number` first scans the subsequent digits (and letters) and stores them in a buffer. This is necessary, because hexadecimal numbers are denoted by the postfix letter H (rather than a prefix character). The postfix letter X specifies that the digits denote a character. There exists one case in the language Oberon, where a look-ahead of a single character does not suffice to identify the next symbol. When a sequence of digits is followed by a period, this period may either be the decimal point of a real number, or it may be the first element of a range (..). Fortunately, the problem can be solved locally as follows: If, after reading digits and a period, a second period is present, the number symbol is returned, and the local variable `ch` is assigned the special value 7FX. A subsequent call of `Get` then delivers the range symbol. Otherwise the reader after the digit sequence belongs to the (real) number. ## 12.6 Searching the symbol table, and handling symbol files ### 12.6.1 The structure of the symbol table The symbol table constitutes the context in which statements and expressions are parsed. Each procedure establishes a scope of visibility of local identifiers. The records registering identifiers belonging to a scope are linked as a linear list. They are of type `Object`. Each object has a type. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 38 Context: ```markdown Types are represented by records of type `Type`. These two types pervade the entire compiler, and they are defined as follows: ```pascal TYPE Object = POINTER TO ObjDesc; Type = POINTER TO TypeDesc; ObjDesc = RECORD class: INTEGER; exp: BOOLEAN; (* exported / read-only *) next: Object; type: Type; name: ORS.Ident; val: INTEGER END; TypeDesc = RECORD form: ref, min: INTEGER; (* ref is only used for import/export *) npl: INTEGER; (* for procedures; extension level for record's *) len: INTEGER; (* for arrays, len = 0 -> open array; for records; adr of descriptor *) desc: typb: Object; base: Type; (* for arrays, records, pointers *) size: INTEGER; (* in bytes; always multiple of 4, except for Byte, Bool and Char *) END; ``` Procedures for generating and searching the lists are contained in module `ORB`. If a new identifier is to be added, procedure `NewObj` first searches the list, and if the identifier is already present, a double definition is diagnosed. Otherwise, the new element is appended, thereby preserving the order given by the source text. Procedures, and therefore also scopes, may be nested. Each scope is represented by the list of its declared identifiers, and the list of the currently visible scopes are again connected as a list. Procedure `OpenScope` appends an element and procedure `CloseScope` removes it. The list of scopes is anchored in the global variable `topScope` and linked by the field `desc`. It is treated like a stack. It consists of elements of type `Object`, each one being the header (`class = Head`) of the list of declared entities. As an example, the procedure for searching an object (with name `ORS.id`) is shown here: ```pascal PROCEDURE ThisObj(): Object; VAR s: Object; BEGIN s := topScope; REPEAT x := s.next; WHILE (x # NIL) AND (x.name # ORS.id) DO x := x.next; s := dsc UNTIL (x # NIL) OR (s = NIL); RETURN x; END ThisObj; ``` A snapshot of a symbol table for an example with nested scopes is shown in Fig. 12.6. It is taken when the following declarations are parsed and when the statement `s` is reached. ```pascal VAR x: INTEGER; PROCEDURE P(): INTEGER; BEGIN END P; PROCEDURE Q(v: INTEGER); BEGIN PROCEDURE R(w: INTEGER); BEGIN B := END R; END; END Q; ``` ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 39 Context: # 12.6.2 Symbol files A search of an identifier proceeds first through the scope list, and for each header its list of object records is scanned. This mirrors the scope rule of the language and guarantees that if several entities carry the same identifier, the most local one is selected. The linear list of objects represents the simplest implementation by far. A tree structure would in many cases be more efficient for searching and would therefore seem more recommendable. Experiments have shown, however, that the gain in speed is marginal. The reason is that the lists are typically quite short. The superiority of a tree structure becomes manifest only when a large number of global objects is declared. We emphasize that when a tree structure is used for each scope, the linear lists must still be present, because the order of declarations is sometimes relevant in interpretation, e.g., in parameter lists. Not only procedures, but also record types establish their own local scope. The list of record fields is anchored in the type record's field des, and it is searched by procedure `thisField`. If a record type `R1` is an extension of `R0`, then `R1`'s field list contains only the fields of the extension proper. The base type `R0` is referenced by the `BaseTyp` field of `R1`. Hence, a search for a field may have to proceed through the field lists of an entire sequence of record base types. The major part of module `ORB` is devoted to input and output of symbol files. A symbol file is a linearized form of an excerpt of the symbol table containing descriptions of all exported (marked) objects. All exports are declared in the global scope. Procedure `Export` traverses the list of global objects and outputs them to the symbol file. The structure of a symbol file is defined by the syntax specified below. The following terminal symbols are class and form specifiers or reference numbers for basic types with fixed values: Classes: - `Cons`: `C` = 1, `Var` = 2, `Par` = 3, `Id` = 4, `Typ` = 5 Forms: - `Byte` = 1, `Bool` = 2, `Char` = 3, `Int` = 4, `Lint` = 5, `Set` = 6 - `Pointer` = 7, `NotBy` = 9, `ProcTyp` = 10, `Array` = 12, `Record` = 13 ```markdown Symbol = null key name versionkey (object) object = (CON name type {value} [ | TYPE name type {fix} 0 | VAR name type expn]) type = ref (PTR type | ARR type len | REC type {field} [ | PRO type {param}]) ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 40 Context: # Field Type Description - **field** = FLD name type offset. - **param** = (VAR | PAR) type. A procedure type description contains a parameter list. Similarly, a record type description with form specifier `Record` contains the list of field descriptions. Note that a procedure is considered as a constant of a procedure type. Objects have types, and types are referenced by pointers. These cannot be written on a file. The straightforward solution would be to use the type identifiers as they appear in the program to denote types. However, this would be rather crude and inefficient, and second, there are anonymous types, for which artificial identifiers would have to be generated. An elegant solution lies in consecutively numbering types. Whenever a type is encountered the first time, it is assigned a unique **reference number**. For this purpose, records (in the compiler) of type `Type` contain the field ref. Following the number, a description of the type is then written to the symbol file. When the type is encountered again during the traversal of the data structure, only the reference number is issued, with a negative sign. The global variable `OBR.Ref` functions as the running reference number. When reading a symbol file, a positive reference number is followed by the type’s description. A pointer to the type read is assigned to the global table `typab` with the reference number as index. When a negative reference number is read (it is not followed by a type description), then the type is identified by `typab[ref]` (see procedure in `Type`). In the following example, types are identified by their reference number (e.g. `#14`), and later referenced by this number (`'14`). ```pascal MODULE A; CONST "10 Dollar" = "$"; TYPE R = RECORD v: INTEGER; v: SET END; END; P = POINTER TO R; A = ARRAY 8 OF INTEGER; B = ARRAY 4 OF REAL; C = ARRAY 10 OF CHAR; V = ARRAY OF INTEGER; PROCEDURE O0; BEGIN END O0; PROCEDURE O1(x: INTEGER); INTEGER; BEGIN RETURN x + y END O1; END A. ``` ### Class Definitions - **class CON**: CON [14] - **class CON**: CON [16] - **class**: TYP = [14] from `REC [14]` environ =`1 extlev = 0 size = 8` | v[6] 4 [14] [0] - **class**: TYP = [15] from `REC [15]` environ = `0 size = 32` | v[60] form = `ARR [14] len = 4 size = 32 [0]` - **class**: TYP = [16] from `ERR [14]` - **class**: TYP = [17] from `ARR [14]` | `len = 8 size = 32` - **class**: TYP = [18] from `ARR [15]` | `len = 5 size = 20` | `len = 4 size = 80` - **class**: TYP = [19] form = `ARR [15]` | `len = 10 size = 320` - **class**: TYP = [20] form = `ARR [15]` | `len = 1 size = 8` - **class**: VAR [14] - **class**: CON [30] from `PRO [14]` - **class**: CON [40] from `PRO [16]` After a symbol file has been generated, it is compared with the file from a previous compilation of the same module, if one exists. Only if the two files differ and if the compiler's option is enabled, is the old file replaced by the new version. The comparison is made by comparing byte after byte without consideration of the file's structure. This somewhat crude approach was chosen because of its simplicity and yielded good results in practice. A symbol file must not contain addresses of variables or procedures. If they did, most changes in the program would result in a change of the symbol file. This must be avoided, because changes in the program result in changes in the symbol file. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 41 Context: the implementation (rather than the interface) of a module are supposed to remain invisible to the clients. Only changes in the interface are allowed to affect changes in the symbol file, requiring recompilation of all clients. Therefore, addresses are replaced by export numbers. The variable `exno` (global in ORP) serves as running number (see ORP.Declarations and ORP.ProcedureDecl). The translation from export number to address is performed by the loader. Every code line contains a list (table) of addresses (of variables and entry points for procedures). The export number serves as index in this table to obtain the requested address. Export numbers are generated by the parser. Objects exported from some module `M1` may refer in their declaration to some other module `M0` imported by `M1`. It would be unacceptable if an import of `M1` would then also require the import of `M0`, i.e., imply the automatic reading of `M0`'s symbol file. It would trigger a chain reaction of imports that must be avoided. Fortunately, such a chain reaction can be avoided by making symbol files self-contained, i.e., by including in every symbol file the description of entities that stem from other modules. Such entities are always types. The inclusion of types imported from other modules seems simple enough to handle: type descriptions must include a reference to the module from which the type was imported. This reference is the name and key of the respective module. However, there exists one additional fact that cannot be ignored. Consider a module `M1` importing a variable `x` from a module `M0`. Let the type `T` of `x` be defined in module `M0`. Also, assume `M1` to contain a variable of type `M0.T`. Evidently, `x` and `y` are of the same type, and the compiler compiling `M2` must recognize this fact. Hence, when importing `M0` during compilation of `M1`, the imported element `T` must not only be registered in the symbol table, but it must also be recognized as being identical to `T` already imported from `M2` directly. It is rather fortunate that the language definition specifies equivalence of types on the basis of names rather than structure, because it allows type tests at execution time to be implemented by a simple address comparison. The measures to be taken to satisfy the new requirements are as follows: 1. Every type element in a symbol file is given a module number. Before a type description is emitted to the file. 2. If a type to be exported has a name and stems from another, imported module, then also the name and key of the module are attached, from which the type stems (see end of procedure ORB.OutType and end of ORB.InType). An additional detail is worth being mentioned here: Hidden pointers. We recall that individual fields of exported record types may be hidden. If marked (by an asterisk) they are exported and therefore visible in importing modules. If not marked, they are not exported and remain invisible, and evidently seem to be ominous in symbol files. However, this is a fallacy. They need to be included in symbol files, although without name, because of meta information to be provided for garbage collection. This is elucidated as follows: Assume that a module `M1` declares a global pointer variables of a type imported from module `M0`. ```pascal MODULE M0; TYPE PTR = POINTER TO Rec0; Rec0 = RECORD p1, q: PTR; ... END; END M0. MODULE M1; VAR p: M0.PTR; R: RECORD M0.Ptr; ... END; END M1. ``` Here `p` and `r` are roots of data structures that must be visited by the garbage collector. If they are not, they will not be marked, and therefore collected with disastrous and entirely unpredictable consequences. The crux is that not only exported pointers (p1, p) must be listed, but also hidden ones (p.q), although they are not accessible in module `M1`. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 42 Context: We chose to include hidden pointers in symbol files without their names, but with their type being of the form `ORB.NIType`. This must be considered in procedure `ORG.FindPrts`, where the condition `typ.form = ORB.Pointer` must be extended to `(typ.form = ORB.Pointer OR (typ.form = ORB.NIType))`. But the story does not end here. Assume that in the example above module `M1` declares a type `Rec1` as an extension of `Mo.Reco`. This requires the generation of a type descriptor. And this descriptor must include not only field `p`, but also the hidden field `d`. This is achieved by also extending the condition `typ.form = ORB.Pointer in ORG.FindPrtFlds` to `(typ.form = ORB.Pointer OR (typ.form = ORB.NIType))`. ## 12.7 The code generator The routines for generating instructions are contained in a single module: ORG. They are fairly numerous, and therefore the interface of ORG is quite large. It is a procedural interface. This implies that there is no "intermediate code" or "intermediate data structure" between parser and code generator. This is one reason for the complexities of the code generator. The other is the regularity and simplicity of the processor architecture. In order to understand the following material, the reader is supposed to be familiar with this architecture (Appendix 2) and the generated code patterns for individual language constructs (Section 12.2). A distinguishing feature of this compiler is that parsing proceeds top-down according to the principle of recursive descent in the parsing tree. This implies that for every syntactic construct a specific procedure is called. It carries the same name as the construct. It also implies that properties of the parsed construct can be represented by parameters of the parsing procedures. Consider, for example, the construct of simple expression: ``` SimpleExpression = term { "+" term }. ``` The corresponding parsing procedure is: ``` PROCEDURE SimpleExpression(VAR x: Item); VAR y: Item; BEGIN itemx := ""; WHILE sym = plus DO ORS.Get(sym); term(y); ORG.AddOp(x, y) END END SimpleExpression ``` The generating procedure `AddOp` receives two parameters representing the operands, and returns the result through the first parameter. This scheme carries the invaluable advantage of using operands efficiently allocated on the stack rather than dynamically allocated on the heap, subject to automatic storage retrieval (garbage collection). Here the processed operands quickly disappear from the stack upon exit from the parser procedure. The parameters representing syntactic constructs are of type `Item` defined in ORG. This data type is rather similar to the type `Object` in ORB. After all, it serves the same purpose; but it represents internal items rather than declared objects. ``` TYPE Item = RECORD mode: INTEGER; type: ORB.TYPE; a: b: INTEGER; rd: BOOLEAN; (* “read only” *) END ``` The attribute class of `Object` is renamed into `Item`. In fact, in some sense different classes evoke different (corresponding) addressing modes as featured by the processor architecture. According to the architecture, additional modes may have to be introduced. Thanks to the simplicity of RISC, only three are needed: - `Reg = 10`: The item `x` is located in register `x.r` - `Reg1 = 11`: The item `x` is addressed indirectly through register `x.r` plus offset `x.a` - `Cond = 12`: The item is represented by the condition bit registers. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 43 Context: # Instructions Instructions are emitted sequentially and emitted by the four procedures `Put0`, `Put1`, `Put2`, `Put3`. They directly correspond to the instruction formats of the RISC processor (see Chapter 11). The instructions are stored in the array code and the compiler variable `pc` serves as running index. ``` PROCEDURE Put0(op, a, b: INTEGER); // format F0 PROCEDURE Put1(op, a, b: INTEGER); // format F1 PROCEDURE Put2(op, a, b: OFF INTEGER); // format F2 PROCEDURE Put3(op, cond: OFF INTEGER); // format F3 ``` ## 12.7.1. Expressions Expressions consist of operands and operators. They are evaluated and have a value. First, a number of make-procedures transform objects into items (see Section 12.3.2). The principal one is `MakeItem`. Typical objects are variables (class, mode = `Var`). Global variables are addressed with base register `SB` (x7 = 13), local variables with the stack pointer `SP` (x7 = 14). VAR-parameters are addressed indirectly; the address is on the stack (class, mode = `Par`, `Ind`). `x.a` is the offset from the stack pointer. Before an operator can be applied to operands, these must first be transferred (loaded) into registers. This is because the RISC performs operations only on registers. The loading is achieved by procedure `load` (and `loadAdr`) in `ORG`. The resulting mode is `Reg`. In allocating registers, a strict stack principle is used, starting with `R0` to `R11`. This is certainly not an optimal strategy and provides ample room for improvement (usually called optimization). The compiler variable `RH` indicates the next free register (top of register stack). Base address `SB` is, as the name suggests, static. But this holds only within a module. It implies that on every transfer to a procedure in another module, the static base must be adjusted. The simplest way is to load `SB` before every external call, and to restore it to its old value after return from the procedure. We chose a different strategy: loading on demand (see below: global variables). If a variable is indexed, has a field selector, is dereferenced, or has a type guard, this is detected in the parser by procedure `selector`. It calls generators `Index`, `Field`, `DeRef`, or `TypeTest` accordingly (see Section 12.3.2 and patterns 1-4 in Section 12.2). These procedures cause item modes to change as follows: | mode transition of x | instructions emitted | construct | |----------------------|--------------------------------|-----------------------------------| | `Index(x, y)` | (y is loaded into x.y) | `ADD y, SP, y` | | | | field designate, add offset to x.a | | `Par -> Reg` | `LDR RH, SP, x.a` | array variable | | | `ADD y, RH, y` | array parameter | | `Reg -> Reg` | `ADD x, x.r, y` | indexed array | ### 2. Field(x, y) (y.mode = Field, y.a = a field offset) | Transition | From | To | Construction | |----------------|---------------|----------------|----------------------------------| | `Var -> Var` | none | Field designator, add offset to x.a | | | `Reg1 -> Reg` | none | add field offset to x.a | | | `Par -> Reg` | none | add field offset to x.b | | ### 3. DeRef(x) | Transition | From | To | Construction | |----------------|-------------|------------------------|---------------------------| | `Var -> Reg` | `LDR RH, SP, x.a` | dereferenced x^ | | | `Par -> Reg` | `LDR RH, SP, x.a` | dereferenced parameter x^ | A fairly large number of procedures then deal with individual operators. Specifically, they are `Not`, `And1`, `And2`, `Or1`, `Or2` for Boolean operators, `Neg`, `AddOp`, `MulOp`, `DivOp` for operations on integers, `RealOp` for operations on real numbers, and `Singleton`, `Set`, `In`, and `SetOp` for operations. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 44 Context: # 12.7.2 Relations RISC does not feature any compare instruction. Instead, subtraction is used, because an implicit comparison with 0 is performed along with any arithmetic (or load) instruction. Instead of \( x < y \) we use \( x - y \). This is possible because, in addition to the computed difference deposited in a register, also the result of the comparison is deposited in the condition flags \( N \) (difference negative) and \( Z \) (difference zero). Relations therefore yield a result item \( x \) with mode `Cond.x < y (rel(jumps))` identifies the relation. Branch instructions (jumps) are executed conditionally depending on these flags. The value \( x.r \) is then used when generating branch instructions. For example, the relation \( x < y \) is translated simply into: ``` LDR R0, SP, x LDR R1, SP, y CMP R0, R0, R1 ``` and the resulting item mode is `x.mode = Cond, x.r = "less"`. (The mnemonic CMP is synonymous with SUB). More about relations and Boolean expressions will be explained in Section 12.7.6. ## 12.7.3 Set operations The type `SET` represents sets of small integers in the range from 0 to 31. Bit \( i \) signals that \( i \) is an element of the set. This is a convenient representation because the logical instructions directly mirror the set operations: AND implements set intersection, OR set union, and XOR the symmetric set difference. This representation also allows a simple and efficient implementation of membership tests. The instructions for the expression \( \text{m IN s} \) is generated by procedure `In`. Assuming the value in register R0, and the set \( s \) in R1, we obtain: ``` ADD R0, R0, 1 ROR R1, R1, R0 // rotate s by i+1 position, the relevant bit moving to the sign bit ``` The resulting item mode is `Cond.x = r - "minus"`. Of some interest are the procedures for generating sets, i.e. for processing \( \{m\}, \{m .. n\} \), and \( \{m, n\} \), where \( m, n \) are integer expressions. We start with \( \{m\} \). It is generated by procedure `Singleton` using a shift instruction. Assuming \( m \) in R0, the resulting code is: ``` MOV R1, 0, 1 LSL R0, R1, R0 // shift 1 by m bits positions to the left ``` Somewhat more sophisticated is the generation of \( \{m .. n\} \) by procedure `Set`. Assuming \( m \) in R0, and \( n \) in R1, the resulting code is: ``` MOV R0, 0, 2 LSL R1, R2, R1 // shift -2 by n bits positions to the left MOV R0, 2, -1 LSL R0, R2, R0 // shift -1 by m bits positions to the left XOR R0, R0, R1 ``` The set \( \{m, n\} \) is generated as the union of \( \{m\} \) and \( \{n\} \). If any of the element values is a constant, several possibilities of code improvement are possible. For details, the reader is referred to the source code of ORG. ## 12.7.4 Assignments #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 45 Context: ``` Statements have an effect, but no result like expressions. Statements are executed, not evaluated. Assignments alter the value of variables through store instructions. The computation of the address of the affected variable follows the same scheme as for loading. The value to be assigned must be in a register. Assignments of arrays (and records) are an exceptional case in so far as they are performed not by a single store instruction, but by a repetition. Consider `y = x`, where `x` and `y` are both arrays of `n` integers. Assuming that the address of `y` is in register `R0`, that of `x` in `R1`, and the value in `R2`. Then the resulting code is: ``` LDR R3, R1, 0 ; source ADD R1, R1, 4 ; increment index STR R3, R0, 0 ; destination ADD R0, R0, 4 ; increment destination SUB R2, R2, 1 ; counter BNE L2 ; branch if not equal ``` ## 12.7.5 Conditional and repetitive statements These statements are implemented using branch instructions (jumps) as shown in Section 12.2, Patterns 5 - 7. In all repetitive statements, backward jumps occur. Here, at the point of return the value of the global variable `ORG.pc` is saved in a local (`l`) variable of the involved parsing procedure. It is retrieved when the backward jump is emitted. We note that branch instructions use a displacement rather than an absolute destination address. It is the difference between the branch instruction and the destination of the jump. A difficulty, however, arises in the case of forward jumps, a difficulty inherent in all single-pass compilers: When the branch is issued, its destination is still unknown. It follows that the branch displacement must be later resolved when it becomes known, when the destination is reached. This is called a `fixup`. Here the method of fixup lists is used. The place of the instruction with still unknown destination is held in a variable `L` for the respective parsing procedure. If several branches have the same destination, `L` is the heading of a list of the instructions to be fixed up, with its links placed in the instructions themselves in the place of the eventual jump displacement. This shows how to fix a statement by an excerpt of `ORP.StaSequence` with local variable `L0`: ``` ELSEIF sym = ORS.IF THEN ORS.Get(sym); expression(x); ORG.FJump(x); StatSequence: L0 := 0; WHILE sym = ORS.ELSE DO ORS.Get(sym); ORG.FJump(L0); ORG.Fixup(x); expression(x); END; IF sym = ORS.Else THEN ORS.Get(sym); ORG.FJump(L0); ORG.Fixup(x); StatSequence ELSE ORG.Fixup(x); END; ORG.FixLink(L0); ``` ### where in module ORG: ``` PROCEDURE FJump(VAR x: Item); (* conditional forward jump *) BEGIN IF x.mode <> Cond THEN loadCond(x) END; PUJ3(BG, negated(x), x.a); FLinkLink(x); x.a := pc-1; END FJump; PROCEDURE JFJump(VAR L: LONGINT); (* unconditional forward jump *) BEGIN PUJ3(BC, 7); L := L - 1; END FJump; PROCEDURE fix(at: VAR LONGINT); BEGIN code[at] := code[at] DIV C24 * C24 + (with MOD C24); END fix; ``` ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 46 Context: ``` PROCEDURE FixLink(L: LONGINT); VAR L1: LONGINT; BEGIN InvalS := 0; WHILE L <> 0 DO L1 := code[L] MOD 40000; FixLink(L1); L := L1 END END FixLink; PROCEDURE Fixup(VAR x: Item); BEGIN FixLink(x.a) END Fixup; In while-, repeat-, and for statements essentially the same technique is used with the support of the identical procedures in ORG. ## 12.7. Boolean expressions In the case of arithmetic expressions, our compilation scheme results in a conversion from infix to postfix notation (x+y ↔=> x+y). This is not applicable for Boolean expressions, because the operators & (AND) and OR are defined as follows: - x & y → if x then y else FALSE - x OR y → if x then TRUE else y This entails that depending on the value of x, y must not be evaluated. As a consequence, jumps may have to be taken across the code for y. Therefore, the same technique of conditional evaluation must be used for conditional statements. In the case of an expression x & y (or x OR y), procedure ORG.And resp. ORG.OR1 must be called just after parsing x (see ORP.Term resp. ORP.SimpleExpression). Only after parsing also can the generators ORG.And resp. ORG.Or2 be called, providing the necessary fixups or forward jumps. PROCEDURE And1(VAR x: Item); (* x := x & a *) BEGIN IF x.mode & Cond THEN loadCond(y) END; Buf3d.Reg, negated(x.a); x.a := pc; FixLink(x.b); x.b := 0 END And1; PROCEDURE And2(VAR x: Item); BEGIN IF x.mode & Cond THEN loadCond(y) END; x.a := merge(y, x.b); x.b := y; x.r := x.y END And2; A negative consequence of this scheme having condition flags in the processor is that when an item with mode Cond has to be transferred into mode Reg, as in a Boolean assignment, an unpleasantly complex instruction sequence must be generated. Fortunately, this case occurs quite rarely. ## 12.7. Procedures Before embarking on an explanation of procedure calls, entries and exits, we need to know how recursion is handled and how storage for local variables is allocated. Procedure calls cause a sequence of frames to be allocated in a stack fashion. These frames are the storage space for local variables. Each frame is headed by a single word containing the return address of the call. This address is deposited in R15 by the call instructions (BL, branch and link). The compiler "knows" the size of the frame to be allocated, and thus merely decrements the stack pointer SP (Y8) by this amount. Upon return, SP is incremented by the same amount, and PC is restored by a branch instruction. In the following example, a procedure P is called, calling itself Q, and Q calling P again (recursion). The stack then contains 3 frames (see Figure 12.7). ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 47 Context: # 12.7 Stack frames Scheme and layout determine the code sequences for call, entry, and exit of procedures. Here is an example of a procedure P with 2 parameters: **Call:** ``` LDR R0, param0 LDR R1, param1 BL P ``` **Prolog:** ``` SUB SP, SP, size // decrement SP STR LNK, SP, 0 // push return adr STR R0, SP, 4 // push parameter 0 STR R1, SP, 8 // push parameter 1 .... ``` **Epilog:** ``` LDR LNK, SP, 0 // pop return adr ADD SP, SP, size // increment SP BR LNK ``` When the call instruction is executed, parameters reside in registers, starting with R0. For function procedures, the result is passed in register R0. This scheme is very efficient; storing the parameters occurs only in a single place, namely at procedure entry, and not before each call. However, it has severe consequences for the entire register allocation strategy. Throughout the compiler, registers **must** be allocated in strict stack fashion. Furthermore, parameter allocation must start with R0. This is a distinct drawback for function calls. If registers are occupied by other values loaded prior to the call, they must be cleared, i.e., the parameters must be saved and reloaded after return. This is rather cumbersome (see procedures ORG.SaveRegisters and ORG.RestoreRegisters). | F(x) | no register saving | |--------------------|-------------------| | F(x) | | | (x+1) + F(x) | register saving necessary | ## 12.8 Type extension Static typing is an important principle in programming languages. It implies that every constant, variable, or function is of a certain data type, and that this type can be derived by reading the program text without executing it. It is the key principle to introduce important redundancy in languages in such a form that a compiler can detect inconsistencies. It is therefore the key element for reducing the number of errors in programs. However, it also acts as a restriction. It is, for example, impossible to construct data structures (arrays, trees) with different types of elements. In order to relax the rule of strictly static typing, the notion of **type extension** was introduced in Oberon. It makes it possible to construct. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 48 Context: TYPE R0 = RECORD u: INTEGER END; R1 = RECORD (R0) u: INTEGER END; We say that R1 is an extension of R0. R0 has the fields u and v, R1 has u, v, and w. The concept becomes useful in combination with pointers. Let ``` TYPE P = POINTER TO R0; P1 = POINTER TO R1; VAR p0: P0; p1: P1; ``` Now it is possible to assign p1 to p0 (because a P1 is always also a P0), but not p0 to p1, because a P0 need not be a P1. This has the simple consequence that a variable of type P0 may well point to an extension of R0. Therefore, data structures can be declared with a base type, P0, as common element type, but in fact they can individually differ; they can be any extension of the base type. Obviously, it must be possible to determine the actual, current type of an element even if the base type is statically fixed. This is possible through a type test, syntactically a Boolean factor: ``` p0 IS P1 ``` (short for `p0 IS R1`) Furthermore, we introduce the type guard. In the present example, the designator `p0.v` is illegal, because there is no field `v` in a record of type P0, even if the current value of p0 is a R1. As this case occurs frequently, we introduce the short notation `p0(P1,p)`, implying a test `p0 IS P1` and an abort if the test is not met. It is important to mention that this technique also applies to formal variable parameters of record type, as they also represent a pointer to the actual parameter. Its type may be any extension of the type specified for the formal parameter in the procedure heading. How are type test and type guard efficiently implemented? Our first observation is that they must consist of a single comparison only, similar to index checks. This in turn implies that types must be identified by a single word. The solution lies in using the unique address of the type descriptor of the (record) type. Which data must this descriptor hold? Essentially, type descriptors (TD) must identify the base types of a given type. Consider the following hierarchy: ``` TYPE T = RECORD ... END; T0 = RECORD (T) ... END; // extension level 1 T1 = RECORD (T) ... END; // extension level 1 T00 = RECORD (T0) ... END; // extension level 2 T10 = RECORD (T1) ... END; // extension level 2 T11 = RECORD (T1) ... END; // extension level 2 ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 49 Context: # Type Hierarchy In the symbol table, the field base refers to the ancestor of a given record type. Thus base of the type representing T11 points to T1, etc. Run-time checks, however, must be last, and hence cannot proceed through chains of pointers. Instead, each TD contains an array with references to the ancestor TDs (including itself). For the example above, the TDs are as follows: - `TD(T)` = `[]` - `TD(TI)` = `[T, TI]` - `TD(T00)` = `[T, T00]` - `TD(T01)` = `[T, T01]` - `TD(T10)` = `[T, T10]` - `TD(T11)` = `[T, T11]` Evidently, the first element can be omitted, as it always refers to the common base of the type hierarchy. The last element always points to the TD's owner. TDs are allocated in the data area, the area for variables. ## References to TDs References to TDs are called type tags. They are required in two cases. The first is for records referenced by pointers. Such dynamically allocated records carry an additional, hidden field holding their type tag. (A second additional word is reserved for use by the garbage collector. The offset of the tag field is therefore -8.) The second case is that of record-typed VAR-parameters. In this case, the type tag is explicitly passed along with the address of the actual parameter. Such parameters therefore require two words/registers. A type test consists of a test for equality of two type tags. In PS, the first tag is that of the n-th entry of the TD of `p`, where `n` is the extension level of `T`. The second tag is that of type `T`. This is shown in Pattern 1 in Section 12.2 (see also Fig. 12.4). The test then is as follows: - `p.tag[tl] = addr(T)`, where `l` is the extension level of `T`. When declaring a record type, it is not known how many extensions, or how many levels will be built on this type. Therefore, TDs should actually be infinite arrays. We decided to restrict them to 3 levels only. The first entry, which is never used for checking, is replaced by the size of the record. ## 12.7.9 Import and Export, Global Variables Addresses of imported objects are not available to the compiler. Their computation must be left to the module loader (see Chapter 6). Similar to handling addresses of imported modules, the compiler puts the necessary information in place of the actual addresses into the instruction itself. In the case of procedure calls, this is quite feasible, because the BL instruction features an offset of 24 bits. The information consists of the module number and the export number of the imported object. In addition, there is a link to the previous instruction referring to an imported procedure. The origin of the first imported circular jump is rooted in the compiler variable `fork`, and the 24 bits in each BL instruction 4 bits are used for the module number, 8 bits for the object's export number, and 12 for the link. The loader need only scan this list to fix up the addresses (jump offsets). Matters are more complex in the case of data. Object records in the symbol table have a field lev. It indicates the nesting level of variables local to procedures. It is also used for the module number in the case of variables of imported modules. Note that when importing, objects designating modules are inserted in the symbol table, and the list of their own objects are attached in the field `disc`. In this #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 50 Context: # 12.7.10 Traps This compiler provides an extensive system of safeguards by providing run-time checks (aborts) in several cases: | trap number | trap cause | |-------------|----------------------------------| | 1 | array index out of range | | 2 | type guard failure | | 3 | array or string copy overflow | | 4 | access via NIL pointer | | 5 | illegal procedure call | | 6 | integer division by zero | | 7 | assertion violated | These checks are implemented very efficiently in order not to downgrade a program's performance. Involved is typically a simple compare instruction, plus a conditional branch (BLR MT). It is assumed that any entry of the module table contains not a base address (module numbers start with 1), but a branch instruction to an appropriate trap routine. The trap number is encoded in bits 47:0 of the branch instruction. The predefined procedure `Assert` generates a conditional trap with trap number 7. For example, the statement `Assert(m = n)` generates: ``` LDR R0, m LDR R1, n CMP R0, R1 BLR R1, 7CH ; branch and link if unequal through R12 (MT), trap number 7 ``` Procedure `New`, representing the operator `NEW`, has been implemented with the aid of the trap mechanism. (This is in order to omit any reference to module `Kernel`, which contains the allocation procedure `New`). The generated code for the statement `NEW(p)` is: ``` ADD R0, R0, SP ; address of p ADD R1, SB, tag ; type tag BLR R1, 7CH ; branch and link unconditionally through R12 (MT), trap number 0 ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 51 Context: # 13 A graphics editor ## 13.1 History and goal The origin of graphics systems as they are in use at this time was intimately tied to the advent of the high-resolution bit-mapped display and of the mouse as pointing device. The author's first contact with such equipment dates back to 1976. The Alto computer at the Xerox Palo Alto Research Center is justly termed the first workstation featuring those characteristics. The designer of its first graphics package was Ch. Thacker who perceived the usefulness of the high-resolution screen for drawing and processing schematics of electronic circuits. This system was cleverly tailored to the needs encountered in this activity, and it was remarkable in its compactness and effectiveness due to the lack of unnecessary facilities. Indeed, its acronym was SIL, for Simple Illustrator. After careful study of the used techniques, the author designed a variant, programmed in Modula-2 (instead of BCPL) for the PDP-11 Computer, thereby ordering and exhibiting the involved data structures more explicitly. In intervals of about two years, that system was revised and grew gradually into the present Draw system. The general goal remained a simple line drawing system: emphasis was placed on a clear structure and increase of flexibility through generalization of existing rather than indiscriminate addition of new features. In the history of this evolution, three major transitions can be observed. The first was the move from a single “window”, the screen, to multiple windows showing different excerpts of the same graphic. This step was performed on the Lilith computer which resembled the Alto in many ways. The second major transition was the application of the object-oriented style of programming, which allowed the addition of new element types to the basic system, making it extensible. The third step concerned the proper integration of the Draw system with Oberon’s text system. The last two steps were performed using Oberon and the Ceres computer. We refrain from exhibiting this evolution and merely present the outcome, although the history might be an interesting reflection of the evolution of programming techniques in general, containing many useful lessons. We stress the fact, however, that the present system rests on a long history of development, during which many features and techniques were introduced and later discarded or revised. The size of the system's description is a poor measure of the effort that went into its construction; deletion of program text sometimes marks bigger progress than addition. The goal of the original SIL program was to support the design of electronic circuit diagrams. Primarily, SIL was a line drawing system. This implies that the drawings remain uninterrupted. However, in a properly integrated system, the addition of modules containing operators that interpret the drawings is a reasonably straightforward proposition. In fact, the Oberon system is ideally suited for such steps, particularly due to its command facility. At first, we shall ignore features specifically tailored to circuit design. The primary one is a macro facility to be discussed in a later chapter. The basic system consists of the modules Draw, GraphicFrames, and Graphics. These modules contain the facilities to generate and handle horizontal and vertical lines, text captions, and macros. Additional modules serve to introduce other elements, such as rectangles and circles, and the system is extensible. Further modules may be introduced to handle further types of elements. ## 13.2 A brief guide to Oberon's line drawing system In order to provide the necessary background for the subsequent description of the Draw system's implementation, a brief overview is provided in the style of a user's guide. It summarizes the facilities offered by the system and gives an impression of its versatility. The system called Draw serves to prepare line drawings. They contain lines, text captions, and other items, and are displayed in graphic viewers (more precisely: in menu viewers' graphic frames). #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 52 Context: # Graphic Viewer Commands The graphic viewer shows an excerpt of the drawing plane, and several viewers may show different parts of a drawing. The most frequently used commands are built-in as mouse clicks and combinations of clicks. Additional commands are selectable from texts, either in viewer menus (title bars) or in the text called **Draw Tool**. Fig. 13.1 shows the display with two graphic viewers at the left and the draw tool text at the right. The mouse buttons have the following principal functions whenever the cursor lies in a graphic frame: - **left:** draw / set caret - **middle:** move / copy - **right:** select ## 13.2.1 Basic Commands The command **Draw.Open** opens a new viewer and displays the graph with the name given as a parameter. We suggest that file names use the extension `.graph`. ### Drawing a Line In order to draw a horizontal or vertical line from P0 to P1, the left key is pressed with the cursor at P0 and, while the key is held, the mouse and cursor is moved to P1. Then the key is released. If P0 and P1 differ in both their x and y coordinates, then the end point is adjusted so that the line is either horizontal or vertical. ### Writing a Caption First, the cursor is positioned where the caption is to appear. Then the left key is clicked, causing a crosshair to appear. It is called the caret. Then the text is typed. Only single lines of text are accepted. The DEL key may be used to retract characters (backspace). ### Selecting Elements Most commands require the specification of operands, and many implicitly assume the previously selected elements - the **selection** - to be their operands. A single element is selected by pointing at it with the cursor and then clicking the right mouse button. This also causes previously selected elements to be deselected. If the left key is also clicked, their selection is retained. This action is called an **intertick**. To select several elements at once, the cursor is moved from P0 to P1 while the right key is held. Then all elements lying within the rectangle with diagonally opposite corners at P0 and P1 are selected. Selected lines are displayed as dotted lines, selected captions (and macros) by inverse video mode. A macro is selected by pointing at its lower left corner. The command is called **sensitive area**. ### Moving Elements To move (displace) a set of elements, the elements are first selected and then the cursor is moved from P0 to P1 while the middle key is held. The vector from P0 to P1 specifies the movement and is called the **displacement vector**. P0 and P1 may lie in different viewers displaying the same graph. Small displacements may be achieved by using the keyboard's cursor keys. ### Copying Elements Similarly, the selected elements may be copied (duplicated). In addition to pressing the middle key indicating the displacement vector, the left key is interlocked. The copy command may also be used to copy elements from one graph into another graph by moving the cursor from one viewer into another viewer displaying the destination graph. A text item may be copied from a text image into a graphic frame and vice-versa. There exist two ways to accomplish this: 1. First the caret is placed at the destination position, then the text is selected and the middle key is interlocked. 2. First the text is selected, then the caret is placed at the destination position and the middle key is interlocked. ### Shifting the Plane You may shift the entire drawing plane behind the viewer by specifying a displacement vector pressing the middle button (like in a move command) and interlocking the right button. | Action | Mouse Button | |----------------------|---------------| | left (draw line) | draw line | | left (no motion) | set caret | #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 53 Context: ``` left + middle copy selected caption to caret left + right set secondary caret middle + left copy selection middle + right shift drawing plane right select area right (no motion) select object right + middle copy caption to caret right + left select without deselection ## 13.2.2. Menu commands The following commands are displayed in the menu (title bar) of every graphic viewer. They are activated by being pointed at and by clicking the middle button. - **Draw.Delete** The selected elements are deleted. - **Draw.Store** The drawing is written as a file with the name shown in the title bar. - **Draw.Restore** The original file is renamed by appending “.Bak”. - **Draw.Ticks** The frame displays a pattern of dots (ticks) to facilitate positioning. The two viewers in Fig. 13.1 display different parts of the same graphic. The second view was obtained from the generic **System.Copy** command and a subsequent shift of the drawing plane. ![Figure 13.1](path/to/image) *Figure 13.1 Display with graphics duplicated viewers* ## 13.2.3. Further commands The following commands are listed in the text **Draw.Tool**, but may appear in any text. - **Draw.Store name** The drawing in the marked viewer is stored as a file with the specified name. ``` Image Analysis: ### Comprehensive Examination of Visual Content #### 1. Localization and Attribution 1. **Image 1:** Position - Top right quadrant of the document. 2. **Image 2:** Position - Bottom of the document, divided into two sections next to each other. 3. **Image 3:** Position - Second section in the bottom right. #### 2. Object Detection and Classification 1. **Image 1:** - **Objects:** Command list with mouse actions. - **Category:** Text and instructions. - **Key Features:** Detailed description of commands using different mouse button combinations on a graphical interface. 2. **Image 2 & 3:** - **Objects:** Screen display of a graphic viewer illustrating various parts of the same graphic. - **Category:** Interface screenshot. - **Key Features:** Display of rectangles, circles, command lists, and other UI elements arranged over four segmented viewer sections. #### 3. Scene and Activity Analysis 1. **Image 1:** - **Scene:** A list of commands and actions achievable by mouse interactions. - **Activities:** Explanation of functionalities such as 'copy selected caption to caret', 'move selection’, and 'select object'. 2. **Image 2 & 3:** - **Scene:** Interface screenshots showing graphic viewer displaying different parts of an identical graphic. - **Activities:** Representation of what users would see on their screens when interacting with graphic viewer commands, the switching, and redrawing of graphic elements. #### 4. Text Analysis 1. **Extracted Text:** - **Top Text:** Describes mouse commands to manipulate graphics. - Example: "left + middle copy selected caption to caret". - **Middle Text:** Menu commands in a graphics viewer. - Example: "Draw.Delete The selected elements are deleted". - **Bottom Text:** Explanation of figure 13.1. - Example: "The two viewers in Fig. 13.1. display different parts of the same graphic." 2. **Content Analysis:** - The text provides detailed instructions and descriptions for using specific commands within a graphic interface. Each command and its action is clearly documented for user comprehension and application in graphic manipulation. #### 9. Perspective and Composition - **Perspective:** - The perspective is a direct, eye-level view commonly used in instructional documents to ensure clarity. - **Composition:** - The composition is methodical, with text sections followed by illustrative aids (images) providing context and examples to the textual instructions. #### 10. Contextual Significance - **Overall Document:** - This page is part of an instructional document on graphics viewers. - **Image Contribution:** - Each image enhances comprehension by visually representing commands and their effects, aiding users in understanding the interface's functionalities. #### 13. Graph Numbers No graphs with numerical lists were present, making this aspect not applicable. ### Additional Aspects Included - **Prozessbeschreibungen (Process Descriptions):** - **Process:** Instructions and commands for manipulating graphics. - **Description:** Step-by-step detailing of graphical command inputs and outputs using the graphical viewer interface. ### Summary: The document is an instructional guide on using a graphics viewer interface, focusing on mouse commands and their associated functions. The images clarify the text by showcasing the practical application within the interface. The commands range from basic selections to more complex tasks like restoring frames and saving drawings. The composition and contextual relevance of the images significantly aid in user comprehension and practical application of these commands. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 54 Context: The subsequent commands change attributes of drawing elements, such as line width, text font, and color, and they apply to the most recent selection. ```markdown Draw.SetWidth w default = 1, 0 < w < 7 Draw.ChangeFont fontname Draw.ChangeColor c Draw.ChangeWidth w (0 < w < 7) The ChangeColor command either takes a color number in the range 1..15 or a string as a parameter. It serves to copy the color from the selected character. ## 13.2.4. Macros A macro is a (small) drawing that can be identified as a whole and be used as an element within a (larger) drawing. Macros are typically stored in collections called libraries, from where they can be selected and copied individually. `Draw.Macro lib mac` The macro `mac` is selected from the library named `lib` and inserted in the drawing at the caret's position. An example for the use of macros is drawing electronic circuit diagrams. The basic library file containing frequently used TTL components is called `TTLO.Lib`, and a drawing showing its elements is called `TTLO.Graph` (see Figure 13.2). ![Figure 13.2 Viewer with circuit macros of TTL0 library](path_to_image) ## 13.2.5. Rectangles Rectangles can be created as individual elements. They are frequently used for framing sets of elements. Rectangles consist of four lines that are selectable as a unit. The attribute commands `Draw.SetWidth`, `System.SetColor`, `Draw.ChangeWidth`, and `Draw.ChangeColor` also apply to rectangles. ``` Image Analysis: ### Analysis of the Visual Content #### 1. **Localization and Attribution:** - **Image 1**: - Located prominently below the text under the section "13.2.4 Macros". - Contains diagrams and text. - Numbered as "Image 1." #### 2. **Object Detection and Classification:** - **Image 1**: - Objects Detected: - Circuit diagrams with several components. - Text boxes and interface elements related to circuit design. - Classification: - Electronic components (logic gates, connectors). - Software interface windows. #### 3. **Scene and Activity Analysis:** - **Image 1:** - Entire Scene: - Depicts a software interface displaying circuit diagrams and components. - Activities: - Designing and viewing electronic circuit diagrams. - Main Actors: - The interface itself and its components like logic gates and connectors are the central focus, representing the activity of macro drawing and circuit design. #### 4. **Text Analysis:** - **Image 1:** - Extracted Text: - Various text snippets such as "Add Macro", "Edit Color", circuit names, pin assignments, etc. - Significance: - The text elaborates on the functions and operations available within the software, aiding users in designing and manipulating the electronic circuits. #### 5. **Diagram and Chart Analysis:** - **Image 1:** - Diagrams: - Electronic circuit diagrams showing connections and logical flow. - Some diagrams present logical gates and their interconnections. - Key Insights: - The diagrams represent simple to moderately complex electronic configurations utilizing TTL components. #### **Ablaufprozesse (Process Flows):** - **Image 1:** - Describes the process of designing electronic circuits using schematic diagrams. - Highlights the use of macros in simplifying complex designs by using predefined components. #### **Prozessbeschreibungen (Process Descriptions):** - The text and diagrams together describe the process of drawing and editing circuit designs using TTL components. Instructions include macro selection and insertion, changing attributes of drawing elements, and utilizing libraries. #### **Typen Bezeichnung (Type Designations):** - **TTL (Transistor-Transistor Logic) Components**: - Specifically mentions TTL components and associated tools like `TTL.Lib` for library and `TTL0.Graph` for example drawing. #### **Trend and Interpretation:** - The textual and visual content together suggest a trend towards using macros to streamline electronics design processes, focusing on reusability and efficiency in creating complex circuits. #### **Contextual Significance:** - **Image 1:** - It offers a visual representation to support the textual explanation regarding the usage of macros in circuit design. - Enhances understanding by providing a practical example (`Figure 13.2`). ### Conclusion The analyzed visual content primarily pertains to electronic circuit design, illustrating the use of macros, attributes modification, and libraries for efficient schematic creation. The diagrams and text together serve an educational purpose, explaining how to utilize features of a specific software for circuit design. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 55 Context: # 13.2.6. Oblique lines, circles, and ellipses Further graphic elements are (oblique) lines, circles, and ellipses. The sensitive area of circles and ellipses is at their lowest point. They are created by the following steps: ## Lines: 1. The caret is placed where the starting point is to lie. 2. A secondary caret is placed at the position of the end. 3. The command `Curves.MakeLine` is activated. ## Circles: 1. The caret is placed where the circle’s center is to lie. 2. A secondary caret is placed, its distance from the center specifying the radius. 3. The command `Curves.MakeCircle` is activated. ## Ellipses: 1. The caret is placed where the center is to lie. 2. A second caret is placed. Its horizontal distance from the first caret specifies one axis. 3. A third caret is placed. Its vertical distance from the first caret specifies the other axis. 4. The command `Curves.MakeEllipse` is activated. # 13.2.7. Spline curves Spline curves are created by the following steps: 1. The caret is placed where the starting point is to lie. 2. Secondary carets are placed at the spline's fixed points (at most 20). 3. The command `Splines.MakeOpen` or `Splines.MakeClosed` is activated. # 13.2.8. Constructing new macros A new macro is constructed and inserted in the library `lib` under the name `mac` as follows: 1. All elements which belong to the new macro are selected. 2. The caret is placed at the lower left corner of the area to be spanned by the macro. 3. A secondary caret is placed at the upper right corner of the area to be spanned. 4. The command `MacroTool.MakeMacro lib` is activated. An existing macro can be decomposed (opened) into its parts as follows: 1. The macro is selected. 2. The caret is placed at the position where the decomposition is to appear. 3. The command `MacroTool.OpenMacro` is activated. # 13.3. The core and its structure Like a text, a graphic consists of elements, subsequently to be called objects. Unlike a text, which is a sequence of elements, a graphic is an unordered set of objects. In a text, the position of an element need not be explicitly indicated (stored); it is recomputed from the position of its predecessor each time it is needed, for example for displaying or selecting an element. In a graphic, each object must carry its position explicitly, as it is independent of any other object in the set. This is an essential difference, requiring a different treatment and much more storage space for an equal number of objects. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 56 Context: Although this is an important consideration in the choice of a representation of a data structure, the primary determinants are the kind of objects to be included and the set of operations to be applied to them. Here, we'll set a sensible starting point. To begin with, there exist only two kinds of objects, namely straight, horizontal and vertical lines, and short texts for labeling lines, called captions. It is surprising how many useful tasks can be fulfilled with only these two types of objects. The typical operations to be performed on objects are creating, drawing, moving, copying, and erasing. Those performed on a graphic are inserting, searching, and deleting an object. For the operations on objects, data indicating an object's position (and possibly color), its length and width in the case of lines, and the character string in the case of captions suffice. For the operations on the graphic, some data structure representing the set of objects must be chosen. Without question, a dynamic structure is most appropriate, and it requires the addition of some linking fields to the record representing an object. Without further deliberation, and with the idea that graphics to be handled with this system can contain hundreds rather than tens of thousands of objects, we chose the simplest solution, the linear list. A proper modularization in connection with information hiding will make it possible to alter this choice without affecting client modules. Although in general the nature of a user interface should not influence the representation chosen for the abstract data structure, we need to take note of the manner in which parameters of certain operations are denoted. It is, for example, customary in interactive graphics systems to select the objects to which an operation is to apply before invoking that operation. Their selection is reflected in their visual appearance in some way, and gives the user an opportunity to verify the selection (and to change it, if necessary) before applying the operation (such as deletion). For an object to be selectable means that it must record a state (selected/unselected). We note that it is important that this state is reflected by visual appearance. As a consequence, the property `selected` is added to every object record. We now specify the data types representing lines and captions as follows and note that both types must be extensions of the same base type in order to be members of one and the same data structure. ``` TYPE ObjectDesc = POINTER TO ObjectDesc; ObjectDesc = RECORD x, y: INTEGER; selected: BOOLEAN; next: ObjectDesc END; Line = POINTER TO LineDesc; LineDesc = RECORD (Object) END; Caption = POINTER TO CaptionDesc; CaptionDesc = RECORD (Object) pos: INTEGER END; ``` Selection of a single element is typically achieved by pointing at the object with mouse and cursor. Selection of a set of objects is achieved by specifying a rectangular area, implying selection of all objects lying within it. In both cases, the search for selected elements proceeds through the linked list and relies on the position and size stored in each object's descriptor. As a consequence, the rule was adopted that every object not only specifies a position through its coordinates `x`, `y`, but also the rectangular area within which it lies (with `width` and `height` it). It is thus easy to determine whether a given point identifies an object, as well as whether an object lies fully within a rectangular area. In principle, each caption descriptor carries the sequence of characters (string) representing the caption. The simplest realization would be an array structured field, limiting the length of captions to some fixed, predetermined value. First, this is highly undesirable (although used in early versions of the system). And second, texts carry attributes (color, font). It is therefore best to use a global "scratch text", and to record a caption by the position and length of the string in this immutable text. A procedure `drawGraphic` to draw all objects of a graphic now assumes the following form: #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 57 Context: ``` PROCEDURE drawObj(obj: Object); BEGIN IF obj IS Line THEN drawLine(obj(Line)) ELSE IF obj IS Caption THEN drawCaption(obj(Caption)) ELSE (* other object types, if any *) END; END drawObj; PROCEDURE drawGraphic(first: Object); VAR obj: Object; BEGIN obj := first; WHILE obj <> NIL DO drawObj(obj); obj := obj.next END; END drawGraphic; The two procedures typically are placed in different modules, one containing operations on objects, the other those on graphics. Here the former is the service module, the latter the former's client. Procedures for, e.g., copying elements, or determining whether an object is selectable, follow the same pattern as drawGraphic. This solution has the unpleasant property that all object types are anchored in the base module. If any new types are to be added, the base module has to be modified (and all clients are to be - at least - recompiled). The object-oriented paradigm eliminates this difficulty by inverting the roles of the two modules. It rests on binding the operations pertaining to an object type to each object individually in the form of procedure-typed record fields as shown in the following sample declaration: ObjectDesc = RECORD x, y, w, h: INTEGER; selected: BOOLEAN; draw: PROCEDURE (obj: Object); write: PROCEDURE (obj: Object; VAR f: Files.Rider); next: ObjectDesc END; The procedure drawGraphic is now formulated as follows: PROCEDURE drawGraphic(first: Object); VAR obj: Object; BEGIN obj := first; WHILE obj <> NIL DO drawObj(obj); obj := obj.next END; END drawGraphic; The individual procedures - in object-oriented terminology called methods - are assigned to the record's fields upon its creation. They need no further discrimination of types, as this role is assumed by the assignment of the procedures upon their installation. We note here that the procedure fields are never changed; they assume the role of constants rather than variables associated with each object. This example exhibits in a nutshell the essence of object-oriented programming, extensibility as its purpose and the procedure-typed record field as the technique. The given solution, as it stands, has the drawback that each object (instance, variable) contains several procedures (of which three are listed), and therefore leads to a storage requirement that should be avoided. Furthermore, it defines once and for all the number of operations applicable to objects, and also their parameters and result types. A different approach with the same underlying principle removes these drawbacks. It employs a single installed procedure which itself discriminates among the operations according to differing types of parameters. The parameters of the preceding solution are merged into a single record called a message. The unified procedure is called a handler, and messages are typically extensions of a single base type Msg. TYPE Msg = RECORD END; DrawMsg = RECORD (Msg) END; WriteMsg = RECORD (Msg: Files.Rider END; ObjectDesc = RECORD x, y, w, col: INTEGER; selected: BOOLEAN; END; ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 58 Context: # Modular Structure of Object Management ## Procedures ### handle ```pascal PROCEDURE handle (obj: Object; VAR M: Msg); next: Object END; ``` ### Handler ```pascal PROCEDURE Handler (obj: Object; VAR M: Msg); BEGIN (* this procedure is assigned to the handle field of every line object *) IF M IS DrawMsg THEN drawLine(obj, Line) ELSIF M IS WriteMsg THEN writeLine(obj, M(WriteMsg).R) ELSE ... END; END; ``` ### drawGraphic ```pascal PROCEDURE drawGraphic(first: Object; VAR M: Msg); VAR obj: Object; BEGIN obj := first; WHILE obj <> NIL DO obj.handle(obj, M); obj := obj.next END END drawGraphics; ``` ## Method Record Structure In the present system, a combination of the two schemes presented so far is used. It eliminates the need for individual method fields in each object record as well as the cascaded IF statement for discriminating among the message types. Yet it allows further addition of new methods for later extensions without the need to change the object's declaration. The technique used is to include a single field (called `obj`) in each record (analogous to the handler). This field is a pointer to a method record containing the procedures declared for the base type. At least one of them uses a message parameter, i.e., a parameter of record structure that is extensible. ### Type Definitions ```pascal TYPE Method = POINTER TO MethodDesc; Msg = RECORD END; Context = RECORD END; Object = POINTER TO ObjectDesc; ObjectDesc = RECORD x, y, w, h: INTEGER; selected: BOOLEAN; method: POINTER TO Method; next: Object; END; MethodDesc = RECORD new: Modules.Command; copy: PROCEDURE (obj: Object; to: Object); draw: handle: PROCEDURE (obj: Object; VAR M: Msg); selectable: PROCEDURE (obj: Object; x, y: INTEGER): BOOLEAN; read: PROCEDURE (obj: Object; VAR R: Files.Rider; VAR C: Context); write: PROCEDURE (obj: Object; con: INTEGER; VAR R: Files.Rider; VAR C: Context); END; ``` A single method instance is generated when a new object type is created, typically in the initialization sequence of the concerned module. When a new object is created, a pointer to its record is assigned to the do field of the new object descriptor. A call then has the form `obj.do.write(obj, R)`. This example exhibits the versatility of Oberon's type extension and procedure variable features very well, and it does so without hiding the data structures involved in a dispensable, built-in run-time mechanism. The foregoing deliberations suggest the system’s modular structure shown in Figure 13.3. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 59 Context: # Clients of module Graphics The modules in the top row implement the individual object types' methods, and additionally provide commands, in particular `Make` for creating new objects. The base module specifies the base types and procedures operating on graphics as a whole. Our system, however, deviates from this scheme somewhat for several reasons: 1. **Implementation of the few methods requires relatively short programs for the basic objects.** Although a sensible modularization is desirable, we wish to avoid atomization, and therefore merge parts that would result in tiny modules with the base module. 2. **The elements of a graphic refer to fonts used in captions and to libraries used in macros.** The writing and reading procedures therefore carry a context consisting of fonts and libraries as an additional parameter. Routines for mapping a font (library) to a number according to a given context on output, and a number to a font (library) on input are contained in module `Graphics`. 3. **In the design of the Oberon System**, a hierarchy of four modules has proven to be most appropriate: - Module with base type handling the abstract data structure. - Module containing procedures for the representation of objects in frames (display handling). - Module containing the primary command interpreter and connecting frames with a viewer. - A command module scanning command lines and invoking the appropriate interpreters. The module hierarchy of the Graphics System is shown together with its analogy, with the Text System: | Function | Graphics | Text | |--------------------|---------------------|------------------| | Command Scanner | Draw | Edit | | Viewer Handler | MenuViewers | MenuViewers | | Frame Handler | GraphicFrames | TextFrames | | Base | Graphics | Texts | As a result, module `Graphics` does not only contain the base type `Object`, but also its extensions `Line` and `Macro`. Their methods are also defined in `Graphics`, with the exception of drawing methods, which are defined in `GraphicFrames`, because they refer to frames. So far, we have discussed operations on individual objects and the structure resulting from the desire to be able to add new object types without affecting the base module. We now turn our attention briefly to operations on graphics as a whole. They can be grouped into two kinds, namely operations involving a graphic as a set, and those applying to the selection, i.e., to a subset only. The former kind consists of procedures `Add`, which inserts a new object, `Draw`, which traverses the set of objects and invokes their drawing methods, `SelectObj`, which searches for an object at a given position, `SelectObj`, which marks an object to be selected, `SelectArea`, which identifies all objects lying within a given rectangular area and marks them, `Selectable`, a Boolean function, and `Enumerate`, which applies the parametric procedure handle to all objects of a graphic. Furthermore, the procedures `Load`, `Store`, `Print`, and `WriteFile` belong to this kind. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 60 Context: The set of operations applying to selected objects only consist of the following procedures: **Deselect**, **DrawSet** (drawing the selection according to a specified mode), **Change** (changing certain attributes of selected objects like width, font, color), **Move**, **Copy**, **CopyOver** (copying from one graphic into another), and finally **Delete**. Also, there exists the important procedure **Open** which creates a new graphic, either loading a graphic stored as a file, or generating an empty graphic. The declaration of types and procedures that have emerged so far are summarized in the following excerpt of the module's interface definition. ```pascal DEFINITION Graphics; (* excerpt without macros *) IMPORT Files, Fonts, Texts, Modules, Display; CONST NameLen = 32; TYPE Graph = POINTER TO GraphDesc; Object = POINTER TO ObjectDesc; Method = POINTER TO MethodDesc; ObjectDesc = RECORD x, y: INTEGER; c: BYTE; selected, marked: BOOLEAN; de: Method END; Msg = RECORD END; WildMsg = RECORD (Msg) w: INTEGER END; ColorMsg = RECORD (Msg) col: INTEGER END; FontMsg = RECORD (Msg) font: Fonts.Font END; Name = ARRAY NameLen OF CHAR; GraphDesc = RECORD sel: Object; time: INTEGER END; Context = RECORD END; MethodDesc = RECORD module, allocator: Name; new: Modules.Command; copy: PROCEDURE (obj: Object); draw: change: PROCEDURE (obj: Object; VAR msg: Msg); selectable: PROCEDURE (obj: Object; x, y: INTEGER): BOOLEAN; read: PROCEDURE (obj: Object; VAR R: Files.Rider; VAR C: Context); write: PROCEDURE (obj: Object; cnt: INTEGER; VAR R: Files.Rider; VAR C: Context); END; Line = POINTER TO LineDesc; LineDesc = RECORD (ObjectDesc) END; Caption = POINTER TO CaptionDesc; CaptionDesc = RECORD (ObjectDesc) pos, len: INTEGER END; VAR width, res: INTEGER; T: Texts.Text; BEGIN PROCEDURE New(obj: Object); PROCEDURE Add(G: Graph; obj: Object); PROCEDURE Draw(G: Graph; VAR M: Msg); PROCEDURE ThoDo(G: Graph; x, y: INTEGER; Object); PROCEDURE Select(G: Graph; obj: Object); PROCEDURE SelectArea(G: Graph; x0, x1, y0, y1: INTEGER); PROCEDURE Deselect(G: Graph); PROCEDURE DrawSet(G: Graph; VAR M: Msg); END; ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 61 Context: ```markdown # 13.4. Displaying Graphics The base module **Graphics** defines the representation of a set of objects in terms of a data structure. The particulars are hidden and allow the change of structural representation by an exchange of this module without affecting its clients. The problems of displaying a graphic on a screen or a printed page are not handled by this module; they are delegated to the client module **GraphicFrames**, which defines a frame type for graphics that is an extension of **Display.Frame**, just like **TextFrames.Frame** is an extension of **Display.Frame**. In contrast to text frames, however, a graphic instead of a text is associated with it. ```plaintext FrameDesc = RECORD (Display.FrameDesc) graph: Graphics.Graph; Xg, Yg, X1, Y1, x, y: INTEGER; marked, ticked: BOOLEAN; END ``` Every frame specifies its coordinates **X, Y** within the display area, its size by the attributes **W** (width) and **H** (height), and its background color. Just as a frame represents a (rectangular) section of the entire screen, it also shows an excerpt of the drawing plane of the graphic. The coordinate origin need coincide with neither the frame origin nor the display origin. The frame's position relative to the graphic plane's origin is recorded in the frame descriptor by the coordinates **Xg, Yg**. The additional, redundant attributes **x, y, X1, Y1** are given by the following invariants, and they are recorded in order to avoid their frequent recomputation. ``` X1 = X + W, Y1 = Y + H X = X + Xg, Y = Y + Yg ``` **X** (and hence also **X1** and **Y1**) are changed when a viewer is modified, i.e., when the frame is moved or resized. **Xg** and **Yg** are changed when the graph's origin is moved within a frame. The meaning of the various values is illustrated in Figure 13.4. ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 62 Context: # Figure 13.4 Frame and Graph Coordinates As a consequence, the display coordinates \( u, v \) of an object \( z \) of a graph displayed in a frame \( f \) are computed as: \[ u = z.x + t.x, \quad v = z.y + t.y \] In order to determine whether an object \( z \) lies within a frame \( f \), the following expression must hold: \[ (X.x \leq u) \& (u + z.w \leq f.X1) \& (Y.y \leq v) \& (v + z.h \leq f.Y1) \] The record field marked indicates whether or not the frame contains a caret. Its display position is recorded in the field called mark. A frame may contain several (secondary) carets; they form a list of individual location descriptors. When an object is displayed (drawn), its state must be taken into consideration in order to provide visible user feedback. The manner in which selection is indicated, however, may vary among different object types. This can easily be realized, because every object (type) is associated with an individual drawing procedure. The following visualizations of selection have been chosen: - Selected lines are shown in a gray tone (raster pattern). - Selected captions are shown with "inverse video". Change of state is a relatively frequent operation, and if possible a complete repainting of the involved objects should be avoided for reasons of efficiency. Therefore, procedures for drawing an object are given a mode parameter, in addition to the obvious object and frame parameters. The parameters are combined into the message record of type `DrawMsg`. ```pascal DrawMsg = RECORD (Graphics.Msg) f: Frame; mode: x, y, col: INTEGER; END; ``` The meaning of the mode parameter's four possible values are the following: - **mode = 0**: draw object according to its state, - **mode = 1**: draw reflecting a transition from normal to selected state, - **mode = 2**: draw reflecting a transition from selected to normal state, - **mode = 3**: erase. In the case of captions, for instance, the transitions are indicated by simply inverting the rectangular area covered by the caption. No rewriting of the captions' character patterns is required. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 63 Context: A mode parameter is also necessary for reflecting object deletion. First, the selected objects are drawn with mode indicating erasure. Only afterwards are they removed from the graphic's linked list. Furthermore, the message parameter of the drawing procedure contains two offsets x and y. They are added to the object's coordinates, and their significance will become apparent in connection with macros. The same holds for the color parameter. The drawing procedures are fairly straightforward and use the four basic raster operations of module Display. The only complication arises from the need to clip the drawing at the frame boundaries. In the case of captions, a character is drawn only if it fits into the frame in its entirety. The raster operations do not test (again) whether the indicated position is valid. At this point we recall that copies of a viewer (and its frames) can be generated by the `System.Copy` command. Such copies display the same graphic, but possibly different excerpts of them. When a graphic is changed by an insertion, deletion, or any other operation, at a place that is visible in several frames, all affected views must reflect the change. A direct call to a drawing procedure indicating a frame and the change does therefore not suffice. Here again, the object-oriented style solves the problem neatly: In place of a direct call a message is broadcast to all frames, the message specifying the nature of the required updates. The broadcast is performed by the general procedure `Viewers.Broadcast(M)`. It invokes the handlers of all viewers with the parameter M. The viewer handlers either interpret the message or propagate it to the handlers of their subframes. Procedure `obj.handle` is called with a control message as parameter when pointing at the object and clicking the middle mouse button. This allows control to be passed to the handler of an individual object. The definition of module `GraphicFrames` is summarized by the following interface: ``` DEFINITION GraphicFrames; IMPORT Display, Graphics; TYPE Frame = POINTER TO FrameDesc; LocDesc = POINTER TO LocDesc; LocDesc = RECORD x, y: INTEGER; next: Location END; FrameDesc = RECORD (Display.FrameDesc) graph: Graphics.Graph; xg, Yx, Y1, y1: x, y: INTEGER; marked, ticked: BOOLEAN; mark: LocDesc END; (* mode = 0 - draw according to selected, 1: normal -> selected, 2: selected -> normal, 3: erase *) DrawMsg = RECORD (Graphics.Msg) t: Frame; x, y, col, mode: INTEGER END; PROCEDURE Restore (F: Frame); PROCEDURE Focus (F: Frame); PROCEDURE Selected (F: Frame); PROCEDURE This(x: INTEGER; F: Frame); PROCEDURE Draw (F: Frame); PROCEDURE Erase (F: Frame); PROCEDURE DrawObj (F: Frame; obj: Graphics.Object); PROCEDURE EraseObj (F: Frame; obj: Graphics.Object); PROCEDURE Change (F: Frame; VAR msg: Graphics.Msg); PROCEDURE Decease (F: Frame); PROCEDURE Deselect (F: Frame); PROCEDURE Macro (VAR Lname, Mname: ARRAY OF CHAR); ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 64 Context: # 13.5 The User Interface Although the display is the prime constituent of the interface between the computer and its user, we chose the title of this chapter for a presentation primarily focused on the computer's input, i.e., on its actions instigated by the user's handling of keyboard and mouse, the editing operations. The design of the user interface plays a decisive role in a system's acceptance by users. There is no fixed set of rules which determine the optimal choice of an interface. Many issues are a matter of subjective judgement, and all too often convention is being mixed up with convenience. Nevertheless, a few criteria have emerged as fairly generally accepted. We base our discussion on the premise that input is provided by a keyboard and a mouse, and that keyboard input is essentially to be reserved for textual input. The critical issue is that a mouse - apart from providing a cursor position - allows to signal actions by the state of its keys. Typically, there are far more actions than there are keys. Some mice feature a single key only, a situation that we deem highly unfortunate. There are, however, several ways to "enrich" key states: 1. **Position.** Key states are interpreted depending on the current position of the mouse represented by the cursor. Typically, interpretation occurs by the handler installed in the viewer covering the cursor position, and different handlers are associated with different viewer types. The handler chosen for interpretation may even be associated with an individual (graphic) object and depend on that object's type. 2. **Multiple clicks.** Interpretation may depend on the number of repeated clicks (of the same key), and/or on the duration of clicks. 3. **Interclicks.** Interpretation may depend on the combination of keys depressed until the last one is released. This method is obviously inapplicable for single-key mice. Apart from position dependence, we have quite successfully used interclicks. A ground rule to be observed is that frequent actions should be triggered by single-key clicks, and only variants of them should be signalled by interclicks. The essential art is to avoid overloading this method. Less frequent operations may as well be triggered by textual commands, i.e., by pointing at the command word and clicking the middle button. Even for this kind of activation, Oberon offers two variations: 1. The command is listed in a menu (title bar). This solution is favored when the respective viewer is itself a parameter to the command, and it is recommended when the command is reasonably frequent, because the necessary mouse movement is relatively short. 2. The command lies elsewhere, typically in a viewer containing a tool text. Lastly, we note that any package such as **Draw** is integrated within an entire system together with other packages. Hence it is important that the rules governing the user interfaces of the various packages do not offer unnecessarily, but that they display common ground rules and a common design "philosophy." Draw's conventions were, as far as possible and sensible, adapted to those of the text system. The right key serves for selecting, left for setting the caret, and the middle key for activating general commands, in this case moving and copying the entire graphic. Inherently, drawing involves certain commands that cannot be dealt with in the same way as text fonts. A character is created by typing on the keyboard; a line is created by dragging the mouse while holding the left key. Interclicks left-middle and right-middle are treated in the same way as in the text system (copying a caption from the selection to the caret), and this is not surprising, because text and graphics are properly integrated, i.e., captions can be copied from texts into graphics and vice-versa. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 65 Context: # 13.6. Macros For many applications, it is indispensable that certain sets of objects may be named and used as objects themselves. Such a named subgraph is called a **macro**. A macro thus closely mirrors the sequence of statements in a program text that is given a name and can be referenced from within other statements: the procedure. The notion of a graphic object becomes recursive, too. The facility of recursive objects is so fundamental that it was incorporated in the base module **Graphics** as the third class of objects. Its representation is straightforward: in addition to the attributes common to all objects, a field is provided storing the head of the list of elements which constitute the macro. In the present system, a special node is introduced representing the head of the element list. It is of type `MacHeadDesc` and carries also the name of the macro and the width and height of the rectangle covering all elements. These values serve to speed up the selection process, avoiding their recomputation by scanning the entire element list. The recursive nature of macros manifests itself in recursive calls of display procedures. In order to draw a macro, drawing procedures of the macro's element types are called (which may be macros again). The coordinates of the macro are added to the coordinates of each element, which function as offsets. The color value of the macro, also a field of the parameter of type `DrawSw`, overrides the colors of the elements. This implies that macros always appear monochrome. An application of the macro facility is the design of schematics of electronic circuits. Circuit components correspond to macros. Most components are represented by a rectangular frame and by labeled connectors (pins). Some of the most elementary components, such as gates, diodes, transistors, resistors, and capacitors are represented by standardized symbols. Such symbols, which may be regarded as forming an alphabet of electronic circuit diagrams, are appropriately provided in the form of a special font, i.e., a collection of raster patterns. Three such areas are shown in Figure 13.5, together with the components from which they are assembled. The definitions of the data types involved are: ``` Macro = POINTER TO MacroDesc; MacroDesc = RECORD (ObjectDesc) mac: MacHead END; MacHead = POINTER TO MacHeadDesc; MacHeadDesc = RECORD name: Name; w: INTEGER; lib: Library END; Library = POINTER TO LibraryDesc; LibraryDesc = RECORD name: Name END; ``` Procedure `DrawMac(mh, mh)` displays the macro with head `mh` according to the draw message parameter `mh` which specifies a frame, a position within the frame, a display mode, and an overriding color. In the majority of applications, macros are not created by their user, but are rather provided from another source, in the case of electronic circuits typically by the manufacturer of the components represented by the macros. As a consequence, macros are taken from a collection (inappropriately) called a **library**. In our system, a macro is picked from such a collection by the command `Draw.Macro` with a library name and a macro name as parameters. It inserts the specified macro at the place of the caret by calling `GraphicFrames.Macro`, which in turn calls `Graphics.Add`. At last, we mention that selection of a macro is visualized by covering with a dot pattern the entire rectangular area occupied by the macro. This emphasizes the fact that the macro constitutes an object as a whole. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 66 Context: The design of new macros is a relatively rare activity. Macros are used rather like characters of a font; the design of new macros and fonts is left to the specialist. Nevertheless, it was decided to incorporate the ingredients necessary for macro design in the basic system. They consist of a few procedures only which are used by a tool module called `MacroTool` (see Section 16.3). `MakeMac` integrates all elements lying within a specified rectangular area into a new macro. `OpenMac` reverses this process by disintegrating the macro into its parts. `InsertMac` inserts a specified macro into a library. `NewLib` creates a new, empty library, and `StoreLib` generates a library file containing all macros currently loaded into the specified library. The details of these operations may be examined in the program listings provided later in this chapter. Summarizing, the following procedures are exported from module `Graphics` related to handling macros: - `PROCEDURE GetLibName(VAR Name: ARRAY OF CHAR; replace: BOOLEAN; VAR Lib: Library);` - `PROCEDURE ThisLib(VAR Library: Name: ARRAY OF CHAR; Mach:);` - `PROCEDURE DrawMac(M: Mach; VAR M: Msg);` And the following are added for creating new macros and libraries: - `PROCEDURE NewLib(Name: ARRAY OF CHAR; VAR Library);` - `PROCEDURE StoreLib(VAR Library: FName: ARRAY OF CHAR);` - `PROCEDURE RemoveLibraries;` - `PROCEDURE OpenMac(Mach: M: Mach; G: Graph; x: VAR INTEGER);` - `PROCEDURE MakeMac(G: Graph; x: y, w: INTEGER; Name: ARRAY OF CHAR; Mach:);` - `PROCEDURE InsertMac(m: Mach; Lib: Library; VAR new: BOOLEAN);` ## 13. Object Classes Although surprisingly many applications can be covered satisfactorily with the few types of objects and the few facilities described so far, it is nevertheless expected that a modern graphics system allow the addition of further types of objects. The emphasis lies here on the avoidance instead of change. New facilities are to be providable by the inclusion of new modules without requiring any kind of adjustment, not even recompilation of the existing modules. In practice, their source code would quite likely not be available. It is the triumph of the object-oriented programming technique that this is elegantly possible. The means are the extensible record type and the procedure variable, features of the programming language, and the possibility to load modules and procedure statements within a program, a facility provided by the operating environment. We call, informally, any extension of the type `Object` a class. Hence, the types `Line`, `Caption`, and `Macro` constitute classes. Additional classes can be defined in other modules importing the type `Object`. In every such case, a set of methods must be declared and assigned to a variable of type `MethodDesc`. They form a so-called method suite. Every such module must also contain a procedure, typically a command, to generate a new instance of the new class. This command, likely to be called `Make`, assigns the method suite to the field of the new object. This successful decoupling of additions from the system's base suffices, almost. Only one further kink is unavoidable: When a new graphic, containing objects of a class not defined in the system’s core, is loaded from a file, then that class must be identified, the corresponding module with its handlers must be loaded - this is called dynamic loading - and the object must be generated (allocated). Because the object in question does not already exist at the time of reading the object's attribute values, the generating procedure cannot possibly be installed in the user’s object, it cannot be a member of the method suite. We have chosen the following solution to this problem: 1. Every new class is implemented in the form of a module, and every class is identified by its module name. Every such module contains a command whose effect is to allocate an object of the class, to assign the message suite to it, and to assign the object to the global variable `Graphics.new`. 2. When a graphics file is read, the class of each object is identified and a call to the respective module's allocation procedure derives the desired object. The call consists of two parts: a call to `Modules.ThisMod`, which may cause the loading of the respective class module `M`, and a call of #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 67 Context: ```markdown # Modules.ThisCommand Then the data of the base type `Object` are read, and lastly the data of the extension are read by a call to the class method read. The following may serve as a template for any module defining a new object class `X`. Two examples are given in Section 13.9, namely **Rectangles** and **Curves**. ```oberon MODULE X; IMPORT Files, Oberon, Graphics, GraphicFrames; TYPE XType = POINTER TO XDesc; XDesc = RECORD (Graphics:ObjectDesc) (* additional data fields *) END; VAR method: Graphics.Method; PROCEDURE New*; VAR x: X; BEGIN NEW(x); x^.do := method; Graphics.new := x END New; PROCEDURE Copy(obj, to: Graphics.Object); BEGIN to^(x^) := obj(x^) END Copy; PROCEDURE Draw(obj: Graphics.Object; VAR msg: Graphics.Msg); BEGIN (* Draw *) END Draw; PROCEDURE Selectable(obj: Graphics.Object; x, y: INTEGER): BOOLEAN; BEGIN (* Selectable *) END Selectable; PROCEDURE Change(obj: Graphics.Object; VAR msg: Graphics.Msg); BEGIN IF msg IS Graphics.ColorMsg THEN obj^ := msg(Graphics.ColorMsg) ELSEIF msg IS THEN ... END END Change; PROCEDURE Read(obj: Graphics.Object; VAR w: Files.Rider; VAR C: Context); BEGIN READ(*read *) END Read; PROCEDURE Write(obj: Graphics.Object; cno: SHORTINT); VAR w: Files.Rider; VAR C: Context; BEGIN Graphics.WriteOBJ(w, con, obj); (* write *) END Write; PROCEDURE Make*; (* "command" *) VAR x: X; VAR gf: GraphicFrames.Frame; BEGIN NEW(x); x^.F.mark := X^.x; x^.F.mark := F.y; x^.w := ...; x^.col := Oberon.CurCol; x^.do := method; GraphicFrames.Defocus(F); Graphics.AddF(graf, x); GraphicFrames.DrawObj(F, x) END Make; BEGIN NEW(method); method.module := "X"; method.allocator := "New"; method.copy := Copy; method.draw := Draw; method.selectable := Selectable; method.handle := Handle; method.read := Read; method.write := Write; method.print := Print END X. ``` We wish to point out that also the macro and library facilities are capable of integrating objects of new classes, i.e., of types not occurring in the declarations of macro and library facilities. The complete interface definition of module `Graphics` is obtained from its excerpt given in Sect. 13.3, augmented by the declarations of types and procedures in Sect. 13.6 and 13.7. ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 68 Context: # 13.8. The implementation ## 13.8.1. Module Draw Module `Draw` is a typical command module whose exported procedures are listed in a tool text. Its task is to scan the text containing the command for parameters, to check their validity, and to activate the corresponding procedures, which primarily are contained in modules `Graphics` and `GraphicFrames`. The most prominent among them is the `Open` command. It generates a new viewer containing two frames, namely a text frame serving as menu, and a graphic frame. We emphasize at this point that graphic frames may be opened and manipulated also by other modules apart from `Draw`. In particular, document editors that integrate texts and graphics - and perhaps also other entities - would refer to `Graphics` and `GraphicFrames` directly, but not make use of `Draw` which, as a tool module, should not have client modules. ``` DEFINITION Draw; PROCEDURE Open; PROCEDURE Delete; PROCEDURE SetWidth; PROCEDURE ChangeColor; PROCEDURE Store; PROCEDURE Macro; PROCEDURE OpenMacro; PROCEDURE MakeMacro; PROCEDURE LoadLibrary; END Draw. ``` ## 13.8.2. Module GraphicFrames Module `GraphicFrames` contains all routines concerned with displaying, visualizing graphic frames and their contents, i.e., graphics. It also contains the routines for creating new objects of the base classes, i.e., lines, captions, and macros. And most importantly, it specifies the appropriate frame handler which interprets input actions and thereby defines the user interface. The handler discriminates among the following message types: 1. **Update messages**. According to the id field of the message record, either a specific object or the entire selection of a graphic are drawn according to a mode. The case `id = 0` signifies a restoration of the entire frame including all objects of the graphic. 2. **Selection, focus, and position queries**. They serve for the identification of the graphic frame containing the latest selection, containing the caret (mark) or the indicated position. In order to identify the latest selection, the time is recorded in the graph descriptor whenever a new selection is made or when new objects are inserted. 3. **Input messages**. They originate from the central loop of module Oberon and indicate either a mouse action (track message) or a keyboard event (consume message). 4. **Control messages** from Oberon. They indicate that all marks (selection, caret, star) are to be removed (neutralize), or that the focus has to be relinquished (defocus). 5. **Selection and copy messages** from Oberon. They constitute the interface between the graphics and the text system, and make possible identification and copying of captions between graphic and text frames. 6. **Modify messages** from `Menu.Viewers`. They indicate that a frame has to be adjusted in size and position because a neighbouring viewer has been reshaped, or because its own viewer has been repositioned. 7. **Display messages**. They originate from procedure `InsertChar` and handle the displaying of single characters when a caption is composed (see below). #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 69 Context: The frame handler receiving a consume message interprets the request through procedure `InsertChar`, and receiving a track message through procedure `Edit`. If no mouse key is depressed, the cursor is simply drawn, and thereby the mouse is tracked. Instead of the regular arrow, a crosshair is used as cursor pattern. Thereby immediate visual feedback is provided to indicate that mouse actions are interpreted by the graphics handler (instead of, e.g., a text handler). Such feedback is helpful when graphic frames appear not only in a menuever, but as subframes of a more highly structured document frame. Procedure `Edit` first tracks the mouse while recording further key activities (interlocks) until all keys are released. The subsequent action is determined by the perceived key clicks. The actions are (the second key denotes the interlocks): - **keys = left, set caret:** set caret, if mouse was not moved, otherwise draw new line - **keys = left, middle:** copy text selection to caret position - **keys = left, right:** set secondary caret (mark) - **keys = middle:** move selection - **keys = middle, left:** copy selection - **keys = middle, right:** shift origin of graph - **keys = right:** select (either object, or objects in area) - **keys = right, middle:** copy selected text to caret position When copying or moving a set of selected objects, it must be distinguished between the cases where the source and the destination graphics are the same or are distinct. In the former case, source and destination positions may lie in the same or in different frames. Procedure `InsertChar` handles the creation of new captions. The actual character string is appended to the global text `T`, and the new object records its position within `T` and its length. A complication arises because the input process consists of as many user actions as there are characters, and because other actions may possibly intervene between the typing. It is therefore unavoidable to record an insertion state, which is embodied by the global variable `newcap`. When a character is typed, and `newcap = NIL`, then a new caption is created consisting of the single typed character. Subsequent typing results in appending characters to the string (and `newcap`). The variable is reset to `NIL` when the caret is repositioned. The BS character is interpreted as a backspace to procedure `DeleteChar`. Since the caption being generated may be visible simultaneously in several frames, its display must be handled by a message. For this reason, the special message `DispMsg` is introduced, and as a result, the process of character insertion turns out to be a rather complex action. To avoid even further complexity, the restriction is adopted that all characters of a caption must use the same attributes (font, color). The definition of the interface of `GraphicFrames` is listed in Section 13.3. ## 13.8.3. Module Graphics The preceding presentations of the interface definitions have explained the framework of the graphics system and set the goals for their implementation. We recall that the core module `Graphics` handles the data structures representing sets of objects without reliance on the specifications of individual objects. Even the structural aspects of the object sets are not fixed by the interface. Several solutions, and hence several implementations are imaginable. Here we present the simplest solution for representing an abstract, unordered set: the linear, linked list. It is embodied in the object record's additional, hidden field next. Consequently, a graphic is represented by the head of the list. The type `GraphDesc` contains the hidden field first (listing of `Graphics`). In addition, the descriptor contains the exported field sel defining a selected element, and the field `time` indicating the time of its selection. The latter is used to determine the most recent selection in various viewers. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 70 Context: Additional data structures become necessary through the presence of macros and classes. Macros are represented by the list of their elements, like graphics. Their header is of type `MacHeadDesc` in analogy to `GraphDesc`. In addition to a macro's name, width, and height, it contains the field `first`, pointing to the list's first element, and the field `lib`, referring to the library from which the macro stems. A library descriptor is similarly structured: in addition to its name, the field `first` points to the list of elements (macros) of the library, which are themselves linked through the field `next`. Fig. 13.6 shows the data structure containing two libraries. It is anchored in the global variable `firstLib`. ``` firstLib ├── Lib0 │ ├── first │ │ └── LibraryDesc │ └── MacHeadDesc │ └── next ├── next └── Lib1 └── first └── ObjectDesc ``` **Fig. 13.6** Data structure for two libraries, each with three macros. Libraries are permanently stored as files. It is evidently unacceptable that file access be required upon every reference to a macro, e.g., each time a macro is redrawn. Therefore, a library is loaded into primary store when one of its elements is referenced for the first time. Procedure `ThisMac` searches the data structure representing the specified library and locates the header of the requested macro. We emphasize that the structures employed for macro and library representation remain hidden from clients, just like the structure of graphics remains hidden within module `Graphics`. Thus, none of the linkage fields of records (`first`, `next`, `sel`) are exported from the base module. This measure retains the possibility to change the structural design decisions without affecting the client modules. But partly it is also responsible for the necessity to include macros in the base module. A large fraction of module `Graphics` is taken up by procedures for reading and writing files representing graphics and libraries. They convert their internal data structure into a sequential form and vice versa. This would be a rather trivial task, were it not for the presence of pointers referring to macros and classes. These pointers must be converted into descriptors that are position-independent, such as names. The same problem is posed by fonts (which are also represented by pointers). Evidently, the replacement of every pointer by an explicit name would be an uneconomical solution with respect to storage space as well as speed of reading and writing. Therefore, pointers to fonts are used. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 71 Context: and libraries - themselves represented as files - are replaced by indices to font and library dictionaries. These dictionaries establish a context and are constructed while a file is read. They are used only during this process and hence are local to procedure `Load` (or `Open`). For classes, a dictionary listing the respective allocation procedures is constructed in order to avoid repeated calls to determine the pertinent allocator. When a graphic file is generated by procedure `Store`, local dictionaries are constructed of fonts, libraries, and classes of objects that have been written onto the file. Upon encountering a caption, a macro, or any element whose font, library, or class is not contained in the respective dictionary, a pair consisting of index and name is emitted to the file, thereby assigning a number to each name. These pairs are interspersed within the sequence of object descriptions. When the graphic file is read, these pairs trigger insertion of the font, library, or class in the respective dictionary, whereby the name is converted into a pointer to the entity, which is obtained by a loading process embodied by procedures `FontsThis`, `Gell.Lib`, and `GetClass`. Both the `Load` and `Store` procedures traverse the file only once. The files are self-contained in the sense that all external quantities are represented by their names. The format of a graphic file is defined in Extended BNF syntax as follows: ``` file ::= tag stretch. stretch ::= {item} 255. item ::= 0 | 1 (font fontname | 0 | libno libname | 0 2 classno classname allocname) | 1 data (font string | 3 data libno macname | classno data extension). data ::= x y w h color. ``` All class numbers are at least 4; the values 1, 2, and 3 are assigned to lines, captions, and macros. `x`, `y`, `w`, and `h` are two-byte integer attributes of the base type `Object`. The attribute `color` takes a single byte. The first byte of an item being 0 signifies that the item is an identification of a new font, library, or class. If the second byte is 0, a new font is announced, if 1 a new library, and if 2 a new class of elements. The same procedures are used for loading and storing a library file. In fact, `Load` and `Store` read a file stretch representing a sequence of elements which is terminated by a special value (255). In a library file, each macro corresponds to a stretch, and the terminator is followed by values specifying the macro's overall width, height, and its name. The structure of library files is defined in the following syntax: ``` library ::= libtag (macro). macro ::= stretch w h name. ``` The first byte of each element is a class number within the context of the file and identifies the class to which the element belongs. An object of the given class is allocated by calling the class' allocation procedure, which is obtained from the class dictionary in the given context. The class number is used as dictionary index. The presence of the required allocation procedure in the dictionary is guaranteed by the fact that a corresponding index/name pair had preceded the element in the file. The encounter of such a pair triggers the loading of the module specifying the class and its methods. The name of the pair consists of two parts: the first specifies the module in which the class is defined, and it is taken as the parameter of the call to the loader (see procedure `GetClass`). The second part is the name of the relevant allocation procedure which returns an object to variable `Graphics.New`. Thereafter, the data defined in the base type `Object` are read. Data belonging to an extension follow those of the base type, and they are read by the extension's read method. This part must always be headed by a type specifying the number of bytes which follow. This information is used in the case where a requested module is not present; it indicates the number of bytes to be skipped in order to continue reading further elements. A last noteworthy detail concerns the `Move` operation which appears as surprisingly complicated, particularly in comparison with the related copy operation. The reason is our deviation from the principle that a graphics editor must refrain from an interpretation of drawings. Responsible for this #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 72 Context: # 13.9 Rectangles and Curves ## 13.9.1 Rectangles In this section, we present two extensions of the basic graphics system that introduce new classes of objects. The first implements rectangles which are typically used for framing a set of objects. They are, for example, used in the representation of electronic components (macros, see Fig. 13.2). Their implementation follows the scheme presented at the end of chapter 13.7 and is reasonably straightforward, considering that each rectangle merely consists of four lines. Additionally, a background raster may be specified. One of the design decisions occurring for every new class concerns the way to display the selection. In this case we chose, in contrast to the cases of captions and macros, not inverse video, but a small square dot in the lower right corner of the rectangle. The data type `Rectangle` contains one additional field: `lv` indicates the line width. In spite of the simplicity of the notion of rectangles, their drawing method is more complex than might be expected. The reason is that drawing methods are responsible for appropriate clipping at frame boundaries. In this case, some of the component lines may have to be shortened, and some may disappear altogether. Procedure `Handle` provides an example of a receiver of a control message. It is activated as soon as the middle mouse button is pressed, in contrast to other actions, which are initiated after the release of all buttons. Therefore, this message allows for the implementation of actions under control of individual handlers interpreting further mouse movements. In this example, the action serves to change the size of the rectangle, namely by moving its lower left corner. ```pascal DEFINITION Rectangles: TYPE Rectangle = POINTER TO RectDesc; RectDesc = RECORD (Graphics.ObjectDesc) lv: INTEGER END; VAR method: Graphics.Method; PROCEDURE New; PROCEDURE Move; END Rectangles. ``` ## 13.9.2 Oblique Lines and Circles The second extension to be presented is module `Curves`. It introduces two kinds of objects: lines which are not necessarily horizontal or vertical, and circles. All are considered to be variants of the same type `Curve`, the variant being specified by the field `kind` of the object record. Selection is indicated by a small rectangle at the end of a line and at the lowest point of a circle. In order to avoid computations involving floating-point numbers and to increase efficiency, Bresenham algorithms are used. The algorithm for a line defined by `bx - ay = 0` (for b ≤ a) is given by the following statements: ```pascal x := x - h; y := (b - a) DIV 2; WHILE x ≤ a DO Dot(x, y); IF h < 0 THEN INC(h, b-a); INC(y) END; ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 73 Context: ```markdown # Bresenham Algorithm for Circle The Bresenham algorithm for a circle given by the equation \(x^2 + y^2 = r^2\) is: ``` x := r; y := 0; d := 1 - r; WHILE x > y DO Dot(x, y); IF d < 0 THEN INC(y); d := d + 2*y + 1; ELSE INC(y); DEC(x); d := d + 2*y - 2*x + 1; END; END ``` # Definition of Curves ```pascal TYPE Curve = POINTER TO CurveDesc; ``` ## CurveDesc Record ```pascal CurveDesc = RECORD (Graphics.ObjectDesc) kind : INTEGER END; ``` - **kind**: 0 (up-line), 1 (down-line), 2 (circle) # VAR ```pascal method: Graphics.Method; ``` ## Procedures ```pascal PROCEDURE MakeLine; PROCEDURE MakeCircle; END Curves. ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 74 Context: # 14 Building and Maintenance Tools ## 14.1 The Startup Process An aspect usually given little attention in system descriptions is the process of how a system is started. Its choice, however, is itself an interesting and far from trivial design consideration and will be described here in some detail. Moreover, it directly determines the steps in which a system is developed from scratch, mirroring the steps in which it builds itself up from a bare store to an operating body. The startup process typically proceeds in several stages, each of them bringing further facilities into play, raising the system to a higher level towards completion. The term for this strategy is **boot strapping** or, in modern computer jargon, **booting**. 1. **Stage 0** is initiated when power is switched on or when the reset button is pressed and released. To be precise, power-on issues a reset signal to all parts of the computer and holds it for a certain time. Pushing the reset button therefore appears like a power-on without power having been switched off. Release of the reset signal triggers the built-in FPGA hardware to load a short configuration bit-stream from a ROM residing on the Spartan board, called the **platform flash**, into the BRAM within the FPGA. This program is called **boot loader**. Being stored in a ROM, it is always present. The BRAM is address-mapped onto an upper part of the address space, and the RISC processor starts execution at this address. 2. In **Stage 1**, the boot loader loads the **inner core**, which consists of modules **Kernel**, **FileDir**, **Files**, and **Modules**. The loader first inspects the link register. If its value is 0, a cold start is indicated. (If the value of the link register is not 0, this signals an abort caused by pressing button 3 on the board). Then loading is skipped and control is immediately returned to the Oberon command loop. The disk (SD-card, SPI) is initialized. The boot loader terminates with a branch to location 0, which transfers control to the just loaded module **Modules**, the regular loader. 3. **Stage 2** starts with the initialization body of module **Modules** which calls the bodies of **Kernel**, **FileDir** and **Files**, establishing a working file system. Then it calls itself, requesting to load the central module **Oberon**. This implicitly causes the loading of its own imports, namely **Input**, **Display**, **Viewers**, **Fonts**, and **Texts**, establishing a working viewer and text system. This loading of the outer core must be interpreted as the continuation of the loading of the inner core. To allow proper continuation, the boot loader has deposited the following data in fixed locations: | Address | Description | |---------|---------------------------------------------------------------| | 0 | A branch instruction to the initializing body of module **Modules** | | 12 | The limit of available memory | | 16 | The address of the end of the module space loaded | | 20 | The current root of the links of loaded modules | | 24 | The current limit of the module area | 4. In **Stage 3**, **Oberon** calls the loader to load the tool module **System**, and with its imports **MenuViewers** and **TextFrames**. The initialization of **System** causes the opening of the viewers for the system tool and the system log. Control then returns to Oberon and its central loop for polling input events. Normal operation begins. The booting process is summarized in Figure 14.1. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 75 Context: # The Four Stages of the Booting Process This describes the normal case of startup. But, how did the boot loader ever get into the platform-flash, and how did the inner core ever get into the boot area of the disk? How did the files of the outer core get into the file store? In fact, how did the file store get initialized? This is described in the following section on building tools. Precisely to solve this problem, the boot loader has been provided with a second source of the boot data. Instead of from the disk, it may be fetched over a data link, in this case the RS-232 data line. This choice is set by switch 0. - **0** load from the "boot track" of the disk (sectors 2 - 63) - **1** load from the RS-232 line (or a network, if available) In case 1, the data stream originates at a host computer, on which presumably the boot file had been generated or even the entire system had been built. In order to keep the boot loader as simple as possible—remember that it is placed in a small flash memory on every workstation and therefore cannot be changed without a special effort—the format of the byte stream representing the inner core must be simple. We have chosen the following structure, which had never to be changed during the entire development effort of the Oberon System because of both its simplicity and generality: - **BootFile** = (block) - **Block** = size address (byte). (size and address are words) The address of the last block, distinguished by size = 0, is interpreted as the address of the starting point of Stage 2. In this step, a module called **Oberon0** is used as the top module, rather than **Modules**. This module communicates with the host computer via the RS-232 line and in addition features various inspection tools. In particular, it contains a command copying the just loaded inner core into the disk (see also Section 14.2). #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 76 Context: Still, how did the hardware configuration data and the boot loader get into the Flash ROM? This step requires the help of proprietary tools of the FPGA manufacturer. Regrettably, their incantation ceremony typically is rather complex. After all necessary Verilog models have been synthesized, the result is the configuration file `RISCTop.bit`. The necessary source files are: - `RISCTop.v` - `RISC.v` - `Multiplier.v` - `Divider.v` - `FP-Multiplier.v` - `FP-Divider.v` - `dbram32.v` - `RS232T.v` - `RS232R.v` - `SP1K.v` - `XGVS.v` - `P2SV.v` - `RISC.ucf` Thereafter, the boot loader is compiled and, together with the result of the configuration of the RISC hardware, loaded into the configuration memory of the FPGA. This Stage 0 is partly done with proprietary software (dependent on the specific FPGA) and is described in a separate installation guide. ``` Stage 0 +-----------------+ | | | RISCTop.bit | | ins1.mem | | | +-----------------+ | v +------------------------+ | FlashRISC_cmd | +------------------------+ | v +-----------------+ | RISC.bit | +-----------------+ | v +----------------------+ +-----------------+ | Xilinx tool | | RS-232 link | | download.cmd | +-----------------+ +----------------------+ | | v | +-------------------+ | | BRAM | | | boot loader | | +-------------------+ | | | v | +-------------------+ | | Memory | | | Kernel File | | | Modules Oberon | | +-------------------+ | v +--------------------+ | Config mem | | FPGA config | +--------------------+ Figure 14.2 Booting from host computer A simple boot loader reading from the RS-232 line and using the stream format described above is shown here: ```pascal MODULE Bootload; IMPORT SYSTEM; CONST MT = 12; SP = 14; MemLim = 0E700H; (* w = 60; len = 60; data = 56; ctrl = 52; ('device addresses') *) PROCEDURE Recint(VAR x: INTEGER); VAR z, y: INTEGER; BEGIN z := 0; y := 4; REPEAT i := 0; REPEAT UNTIL SYSTEM.BIT(crt, 0); SYSTEM.GET(data, y); z := ROR(z,y,8) UNTIL i = 0; x := z END Recint; PROCEDURE Load; VAR len, adr, dat: INTEGER; BEGIN Recint(len); WHILE len > 0 DO Recint(dat); REPEAT Recint(data); SYSTEM.PUT(dat, adr); adr := adr + 4; len := len - 4 UNTIL len = 0; Recint(len) END Load; ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 77 Context: # 14.2. Building Tools Let us summarize the prerequisites for startup: 1. The FPGA configuration and bootloader must reside in the ROM (platform flash). 2. The boot file must reside on the boot area of the disk. 3. The modules of the outer core must reside in the file system. 4. The default font and `System.Tool` must be present in the file system. These conditions are usually met. But they are not satisfied, if either a new, bare machine is present, or if the disk store is defective. In these cases, the prerequisites must be established with the aid of suitable tools. The tools needed for the case of the bare machine or the incomplete file store are called **building tools**, those required in the case of defects are called **maintenance tools**. Building tools allow to establish the preconditions for the boot process on a bare machine. Establishing condition 1 requires a tool for downloading the hardware configuration of the FPGA resulting from circuit synthesis, and it requires a compiler for generating the boot loader. Condition 0 is established in Stage 0. Establishing condition 1 requires a tool for composing the boot file, and one to load it into the boot area. The former is the compiler, presumably running on a host computer. The resulting files are linked by a linker (ORL) generating a “binary” file. This file is then downloaded from the host computer to the RISC running in boot loader. Here we use an extended inner core, where the main module is not **Modules**, but **Oberon**. The reason is that Oberon allows to perform the subsequent stages by accepting commands over a communication channel (here the RS-232 line). Hence, for the following stages, the tool on the RISC is Oberon, communicating with the host computer's module ORC. Establishing condition 2 implies the building of a file directory and the loading of files. The pair **Oberon** and **ORC** contains commands for initializing a file system, for loading files over the line connection, and for moving the inner core to the disk's boot area. In addition, Oberon contains further commands for file system, memory, and disk inspection. Note that loading (and starting) Oberon automatically starts the entire Oberon system. There remains the important question of how Oberon0 is loaded onto a bare machine. It is done by the boot loader with switch 1 being up. The boot file contains the inner core with the top module being Oberon rather than Modules. The procedure is the following: 1. Select the alternative boot source by setting switch 0 = 1. 2. Reset and send the boot file from the host. The boot file is transferred and Oberon0 is started. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 78 Context: 3. Read all files from the host (which supposedly holds all files needed for the outer core). 4. Invoke the command which loads Oberon. This loads the outer core, sets up the display, and starts the central loop. A more modern solution would be to select the network as an alternative boot file source. We rejected this option in order to keep net access routines outside the ROM, in order to keep the startup of a computer independent of the presence of a network and foreign sources, and also in consideration of the fact that there exist machines which operate in a stand-alone mode. As it turns out, the need for the alternative boot file source arises very rarely. The boot linker ORL, presumably running on a host computer, where the FPGA-tools are available, is almost identical to the module loader, with the exception that object code is not deposited in newly allocated blocks, but is output in the form a file. The name of the top module of the inner core is supplied as a parameter. * ORL.Link Modules generates the regular boot file. * ORL.Link Oberon0 generates the build-up boot file. Oberon0 imports two modules taking care of communication with ORL on the host computer. They are the basic module RS232, and module PCLink1 for file transfer. The latter constitutes a task, accepting commands over the line from ORL. Their interfaces are shown below: ```pascal DEFINITION RS232: PROCEDURE SendX(VAR: BYTE); PROCEDURE Rec(VAR: BYTE); PROCEDURE SendInt(VAR: INTEGER); PROCEDURE RecInt(VAR: INTEGER); PROCEDURE SendReal(VAR: REAL); PROCEDURE SendStr(VAR: ARRAY OF CHAR); PROCEDURE RecInt(VAR: INTEGER); PROCEDURE RecReal(VAR: REAL); PROCEDURE RecStr(VAR: ARRAY OF CHAR); PROCEDURE Line; PROCEDURE End; END RS232; DEFINITION PCLink1: PROCEDURE Run*; PROCEDURE Stop; END PCLink1; ``` The command interpreter is a simple loop, accepting commands specified by an integer followed by parameters which are either integers or names. User-friendliness was not attributed any importance at this point, and it would indeed be merely luxury. We refrain from elaborating on further details and concentrate on providing a list of commands provided by Oberon0. This should give the reader an impression of the capabilities and limitations of this tool module for system initiation and for error searching. (name stands for a string, and a, secno, n stand for integers.) | parameters | action | |------------|----------------------------| | 0 | send and mirror s | | 1 | a, n show (in hex) M[a], M[a+1], …, M[a + n] | | 2 | w fill display with words w | | 3 | secno show disk sector | | 4 | filename read file | | 6 | - start PC-link | | 7 | - show allocation, not sectors, switches, and timer | | 10 | - list modules | | 11 | modname list commands | | 12 | prefix list files (enumerate directory) | | 13 | filename delete file | #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 79 Context: # 14.3 Maintenance Tools An important prerequisite for Stage 2 (and the following stages) in the boot process has not been mentioned above. Recall that the initialization of module `FileDir` constructs the disk sector reservation table in the `Kernel` from information contained on the disk. Obviously, its prerequisite is an intact, consistent file directory. A single unreadable, corrupted file directory or file header sector lets this process fail, and booting becomes impossible. To cope with this (fortunately rare) situation, a maintenance tool has been designed: module `DiskCheck`. ## DiskCheck `DiskCheck` is organized similarly to Oberon as a simple command interpreter, but it imports only `Kernel` and `RS232`. Hence, booting involves only Stages 1 and 2 without any access to the disk. Operating `DiskCheck` requires care and knowledge of the structure of the file system (Chapter 7). The available commands are as follows: | parameters | action | |------------|---------------------------------| | 0 | send and mirror integer (test) | | 1 a, n | show (in hex) M[a], M[a+4], ..., M[a + n*4] | | 2 secno | show disk sector | | 3 secno | show head sector | | 4 secno | show directory sector | | 5 | traverse directory | | 6 secno | clear header sector | | 7 secno | clear directory (root pack) | The essential command is the file directory traversal (5). It lists all faulty directory sectors, showing their numbers. It also lists faulty header sectors. No changes are made to the file system. If a faulty header is encountered, it can subsequently be cleared (6). Thereby, the file is lost. It is not removed from the directory, though. But its length will be zero. Program `DiskCheck` must be extremely robust. No data read can be assumed to be correct, no index can be assumed to be within its declared bounds, no sector number can be assumed to be valid, and no directory or header page may be assumed to have the expected format. Guards and error diagnostics take a prominent place. Whereas a faulty sector in a file in the worst case leads to the loss of that file, a fault in a sector carrying a directory page is quite disastrous. Not only because the files referenced from that page, but also those referenced from descendant pages become inaccessible. A fault in the root page even causes the loss of all files. The catastrophe is of such proportions that measures should be taken even if the case is very unlikely. After all, it may happen, and it indeed has occurred. The only way to recover files that are no longer accessible from the directory is by scanning the entire disk. In order to make a search at all possible, every file header carries a mark field that is given a fixed, constant value. It is very unlikely, but not entirely impossible, that data sectors which happen to have the same value at the location corresponding to that of the mark, may be mistaken to be headers. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 80 Context: The tool performing such a scan is called **Scavenger**. It is, like **DiskCheck**, a simple command interpreter with the following available commands: | parameters | action | |------------|--------------------------------------| | 0 s | send and mirror integer (test) | | 1 n | Scan the first n sectors and collect headers | | 2 - | Display names of collected files | | 3 - | Build new directory | | 4 - | Transfer new directory to the disk | | 5 - | Clear display | During the scan, a new directory is gradually built up in primary store. Sectors marked as headers are recorded by their name and creation date. The scavenger is the reason for recording the file name in the header, although it remains unused there by the Oberon System. Recovery of the date is essential because several files with the same name may be found. If one is found with a newer creation date, the older entry is overwritten. Command **W** transfers the new directory to the disk. For this purpose, it is necessary to have free sectors available. These have been collected during the scan: both old directory sectors (identified by a directory mark similar to the header mark) and overwritten headers are used as free locations. The scavenger has proven its worth on more than one occasion. Its main drawback is that it may rediscover files that had been deleted. The deletion operation by definition affects only the directory, but not the file. Therefore, the header carrying the name remains unchanged and is discovered by the scan. All in all, however, it is a small deficiency. ## Reference 1. N. Wirth. Designing a System from Scratch. *Structured Programming*, 1, (1989), 10-18. #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 81 Context: # 15 Tool and service modules In this chapter, a few modules are presented that do not belong to Oberon's system core. However, they belong to the system in the sense of being basic, and of assistance in some way, either to construct application programs, to communicate with external computers, or to analyze existing programs. ## 15.1 Basic mathematical functions Module `Math` contains the basic standard functions that had been postulated already in 1960 by Algol 60. They are: - `sqrt(x)` | the square root - `exp(x)` | the exponential function - `ln(x)` | the natural logarithm - `sin(x)` | the sine function - `cos(x)` | the cosine function They are presented here only briefly without discussing their approximation methods. However, we point out how advantage can be taken from knowledge about the internal representation of floating-point numbers. ### 15.1.1 Conversion between integers and floating-point numbers The Oberon System adopts the standard format postulated by IEEE. Here we restrict it to the 32-bit variant. A floating-point number `x` consists of 3 parts: - `s` | the sign | 1 bit - `e` | the exponent | 8 bits - `m` | the mantissa | 23 bits Its value is defined as \[ x = (-1)^s \times 2^{e-127} \times (1.m) \] A number is in normalized form, if its mantissa satisfies \(1.0 \leq m < 2.0\). It is assumed that numbers are always normalized, and therefore the leading 1-bit is omitted. The exception is the singular value 0, which cannot be normalized. It must therefore be treated as a special case. It follows that integers and floating-point numbers are represented quite differently, and that conversion operations are necessary to transfer a number from one format to the other. This is the reason why the Oberon language keeps the two types INTEGER and REAL separate. Conversion must be explicitly specified by using the two predefined functions: - \( n = \text{FLOOR}(x) \quad \text{REAL} \rightarrow \text{INTEGER} \) - \( x = \text{FLT}(n) \quad \text{INTEGER} \rightarrow \text{REAL} \) Note: `FLOOR(x)` rounds toward -inf. For example `FLOOR(1.5) = 1`, `FLOOR(-1.5) = -2`. The RISC processor does not feature specific instructions implementing these functions. Instead, the compiler generates inline code using the FAD instruction with special options suppressing normalization. This option is specified by the `u` and `v` modifier bits of the instruction. The `FLOOR` function is realized by adding 0 with an exponent of 127 + 24 and suppressing the insertion of a leading 1-bit (i.e. `u = 1`). This causes the mantissa of the argument to be shifted right until its exponent is equal to 151. The RISC instructions are: ``` MOV R1 R0 4800H R1 = 48000000H FAD R0 R0 R1 ``` The `FLT` function is implemented also by adding 0 with an exponent of 151 and forced insertion of a leading 1-bit (i.e. `v = 1`). #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 82 Context: # 15.1 Floating-Point Number Procedures ## 15.1.1 Packing and Unpacking Procedures ``` MOV R1, 4800H FAD R0, R0 There are two predefined procedures for packing and unpacking a floating-point number: - **PACK(x, e)**: \( x = x \times 2^e \) (for \( x > 0 \)) - **UNPK(x, e)**: assign to \( x \) and \( e \), such that \( x \times 2^e = x_0 \), where \( x_0 \) is the original value of \( x \), \( x \) becomes normalized, that is, \( 1.0 \leq x < 2.0 \) Assuming \( R0 \) = \( x \) and \( R1 \) = \( e \), the instruction sequence for **PACK(x, e)** is: ``` LSL R1, R1, 23 ADD R0, R0, R1 STR R0, R0 ``` Again assuming \( x = 0 \), the instruction sequence for **UNPK(x, e)** is: ``` ASR R1, R0, 23 SUB R1, R1, 127 STR R1, R1 LSL R1, R1, 23 SUB R0, R0, R1 STR R0, R1 ``` ## 15.1.2 The Square Root Function We rely on the definition \( x = 2^m \times r \). Using the intrinsic **UNPK** procedure, the components \( m \) and \( e \) are obtained from \( x \). Then the square root is computed according to the formulas: - \( \text{sqrt}(x) = 2^{\frac{m}{2}} \times \text{sqrt}(r) \) if \( e \) is even, - \( \text{sqrt}(x) = 2^{\frac{m-1}{2}} \times \text{sqrt}(r) \) if \( e \) is odd. The advantage is that the argument of the square root now lies in the narrow interval \( [1.0, 2.0] \), and therefore is easier and faster to approximate by a continued fraction. ``` PROCEDURE sqrt: REAL; REAL; CONST c1 = 0.707106781; (1 / sqrt(2)) c2 = 0.509803926; (sqrt(2)) c3 = 1.414213135; (sqrt(2)) VAR r: REAL; i: INTEGER; BEGIN ASSERT(r >= 0.0); IF r > 0.0 THEN UNPK(x); s := x*(1+x1); s := s + (s*y); s := 0.5 * (s + x*s); IF ODD(i) THEN s := c3 * s; END; PACK(s, DW 2) ELSE s := 0.0; END; RETURN s; END sqrt; ``` ## 15.1.3 The Exponential Function Since our floating-point format is based on an exponent of 2, we first use the formula: ``` exp(x) = \( e^{x} \) = \( x \times \log_e(x) = x / \ln(2) \) \quad \log_e(2) = 1.442695 ``` and first compute \( y_0 \). We decompose \( y_0 \) into its integral part \( = \text{FLOOR}(y) \) and its fractional part \( y - n \). Since \( 2^y \times 2^e = 2^{y_0} \), the result is the sum of the exponent \( n \) and the mantissa \( 2^{y} \). Again, the advantage of the decomposition is that the argument of \( y_0 \) of the polynomial approximation lies in the narrow interval \( [1.0, 2.0] \). #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 83 Context: ``` PROCEDURE expX: REAL; REAL; CONST c1 = 1.44269504088896; // (1 / ln(2) ) p0 = 1.58197615902832; p1 = 2.20257774822299; p2 = 3.2081636692042E-2; q0 = 4.3688938420652; q1 = 3.3318938202161E-2; VAR n: INTEGER; p, y: REAL; BEGIN n := FLOOR(x * c1); y := FLT(n); y := y - y * y / (p0 + (p1 * y + p0) * y); p := (p0 + q1 * y + q0 - p) * 0.5; PACK(p, n); RETURN p END expX; 15.1 The logarithm Again we take advantage of the presence of an exponent in the floating-point representation and use the equations ln (a * b) = ln a + ln b ln (2^x) = log(2^x * m) x ln(2) = x * ln(2) + ln m PROCEDURE lnX: REAL; REAL; CONST c1 = 0.707106781186547; // (1 / sqrt(2) ) c2 = 0.693147180559945; // (ln(2) ) p0 = 0.1617496772011; p1 = 9.9466599001E-1; p2 = 3.843770041E-1; q0 = 4.589734456E1; q1 = 1.678165560E1; q2 = -2.076339789E1; VAR x: REAL; j: INTEGER; y: REAL; BEGIN ASSERT (x > 0.0); UNPK(x, e); IF x < (THRESHOLD * 2.0) = e * 1.0E-1 THEN x := x * 1.0E-1; y := x * (FLT(y) + x / ((p2 * x + p0) / ((x + q2) * x + q1) + q0)); RETURN y END lnX; 15.1. The sine function Figure 15.1 Sine function y = sin(x) First, the argument x is transposed into the interval [0, π/4] by computing ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 84 Context: ```markdown n := FLOOR(y + 0.5); y := (y - n) and then distinguish between two approximating polynomials depending on whether x < π/4. ```pascal PROCEDURE sink(VAR y: REAL; REAL); CONST p0 = 3.86917671E-1; (* (2/pi) *) p1 = 7.85395616E-2; p2 = 2.39904032E-3; p3 = 3.15796226E-5; p4 = 1.336126E-7; p5 = 1.75719436E-9; p6 = 6.67711094E-12; q0 = 9.999999E-1; q1 = 4.848149E-1; q2 = 1.583434E-2; q3 = 3.2599149E-4; q4 = 3.859915E-6; q5 = 2.4603875E-8; q6 = 1.136138E-10; VAR r: INTEGER; yv: REAL; BEGIN r := y < 0; IF y < 0 THEN n := FLOOR(y + 0.5) ELSE n := FLOOR(y - 0.5); y := y - 2.0 * r * y; IF (ord(y) < 0) THEN y := (((((p0*y + p1)*y + p2)*y + p3)*y + p4)*y + p5)*y + p6) / (((((q0*y + q1)*y + q2)*y + q3)*y + q4)*y + q5)*y + q6) ELSE y := (((((p5*y + p4)*y + p3)*y + p2)*y + p1)*y + p0) / (((((q6*y + q5)*y + q4)*y + q3)*y + q2)*y + q1)*y + q0)); IF ODODIN(DIV y) THEN END; RETURN; END; ``` 15.2 A data link -------------- Module **PCLink** serves to transfer data (files) to and from another system. Data are transmitted as a sequence of blocks. Each block is a sequence of bytes. The number of data bytes lies between 0 and 255. They are preceded by a single byte indicating the length. Blocks are 255 bytes long, except the last block, whose length is less than 255. Here, the transmission channel is an RS-232 line. The interface consists of two registers, one for a data byte (address = -56), and one for the status (address = -52). Bit 0 of this status register indicates whether a byte had been received. Bit 1 of the status register indicates, whether the byte in the data register has been sent. (Note: the default transmission rate of the RISC is 9600 b/s). This module represents a server running as an Oberon task which must be activated by the command run. A server running on the partner system must be the master issuing requests. The command sequence is a REC byte, a SND byte, or a REQ byte for letting the connection. REC and SND must be followed by a file name, and the sequence of blocks. Every block is acknowledged by the receiver sending an ACK byte, for which the sender waits before sending the next block. There is no synchronization between blocks. Because the writing into a file may involve operations of unpredictable duration, the received bytes are not written to the file immediately. They are buffered and only output after the entire block has been received. ```pascal MODULE PCLINK; (* NW 8.2.2018 for Oberon on RISC *) IMPORT SYSTEM, Files, Texts, Oberon; CONST data = -56; (* -52 *) blink = 255; VAR REQ = 20H; (* 21H = 21H; SND = 22H; ACK = 10H; NAK = 11H *) VAR O: Oberon.Task; X: TextWriter; PROCEDURE Rec(VAR x: BYTE); BEGIN ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 85 Context: ``` REPEAT UNTIL SYSTEM.BIT(stat, 0); SYSTEM.GET(data, x); END Dec; PROCEDURE RecName(VAR s: ARRAY OF CHAR); VAR i: INTEGER; x: BYTE; BEGIN i := 0; RecX(i); WHILE x <> 0 DO BEGIN s[i] := CHR(x); INC(i); RecX; END; s[i] := 0; END RecName; PROCEDURE Send; BYTE; BEGIN REPEAT UNTIL SYSTEM.BIT(stat, 1); SYSTEM.PUT(data, x); END Send; PROCEDURE Task; VAR len, n: INTEGER; x, ack, code: BYTE; name: ARRAY[0..31] OF CHAR; F: Files.File; Ri: Files.Rider; buf: ARRAY[0..255] OF BYTE; BEGIN IF SYSTEM.BIT(stat, 0) THEN (* "byte available" *) Rec(code); code := SND THEN (* "send file" *) RecName(name); F := Files.Old(name); IF F = NIL THEN Send(ACK); len := Files.Length(F); Files.SetR(F, 0); REPEAT IF len > 1k THEN len := 1k; ELSE len := len; Send(len); n := len; len := len - 1; WHILE n > 0 DO Files.Read(buf, x); Send(buf); DEC(n); END UNTIL len < 1k; ELSE Send(1); END END; ELSE code := REC THEN (* "receive file" *) RecName(name); IF F = NIL THEN Files.SetR(F, 0); Send(ACK); REPEAT Rec(len); len := x - 1; WHILE len > 0 DO buf[i] := 0; INC(i); END; WHILE i < len DO Files.WriteByte(F, buf[i]); INC(i); END UNTIL len < 255; Files.Register(F); Send(ACK); ELSE Send(NAK); END END; ELSE code := REC THEN Send(ACK) (* "for testing" *) END; END; END Task; PROCEDURE Run; BEGIN Oberon.Inst(); Texts.WriteString(W, "PCLink started"); Texts.WriteLn(""); Texts.Append(Oberon.Log, W.buf); END Run; PROCEDURE Stop; ``` #################### File: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf Page: 86 Context: # 15.3. A generator of graphic macros The module `MacroTool` serves to create macros for the graphic system (Ch. 13). It provides the commands `OpenMacro`, `MakeMacro`, `LoadLibrary`, and `StoreLibrary`. - **OpenMacro** decomposes the selected macro into its elements and places them at the position of the caret. This command is typically the first if an existing macro is to be modified. - **MakeMacro** `M` collects all selected objects in the frame designated by the star pointer and unites them into macro `M`. This macro is displayed at the caret position and inserted into library `L`. If no such library exists, a new one is created. - **LoadLibrary** `L` loads the library `L` (under file name `L.Lib`). Note that a library must have been stored before it can be loaded. - **StoreLibrary** stores library `L` (with filename `L.Lib`). The required modules are `Texts`, `Oberon`, `Graphics`, `GraphicFrames`. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 1 Context: # Chapter 5 Interactive Programs and Signals ## Concepts Covered - Software tools, daemons, - interactive programs - non-blocking reads, signals, - signal, sigaction, kill, fflush, raise - sigemptyset, sigfillset, sigaddset, - sigdelset, sigprocmask, sleep ## 5.1 The Different Types of UNIX Programs Programs that run in a UNIX environment can be classified by their relationship to terminal devices and by their input/output streams. They generally fall into one of three categories: **software tools**, **daemons**, and **interactive user programs**. ### 5.1.1 Software Tools (Filters) A software tool is a program that - receives its input from either - one or more files given as command-line arguments, or - from standard input if no filename arguments are present, - expects its input to be an unstructured stream of bytes, almost treated as plain text, and - puts its output, which is also a stream of bytes, usually plain text, on standard output. Because software tools can read from standard input and write to standard output, they can be connected via shell pipes to form pipelines, like factory assembly lines. UNIX has many software tools, including `awk`, `cat`, `cut`, `du`, `fold`, `grep`, `od`, `sed`, `sort`, `tr`, and `uniq`. ### 5.1.2 Daemons Another class of programs are device drivers, which are not even attached to a terminal. A program that is not attached to a terminal is called a daemon in UNIX. A commonly used, but inaccurate, definition of a daemon is that it is a "background" process. To be precise, it is a process that executes without an associated terminal or login shell, waiting for an event to occur. The event might be a user request for a service such as printing or connecting to the Internet, or a clock tick indicating that it is time to run. The word "daemon" is from Greek mythology and refers to a lesser god who did helpful tasks for the people he or she protected. Daemons are like these lesser gods; they are created at boot time and exist, in hiding, ready to provide services when called upon. This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Comprehensive Examination of the Attached Visual Content #### 1. Localization and Attribution: - **Page Layout:** The page consists of a single image, made up entirely of text. - **Image Identification:** - **Image 1:** Occupies the entirety of the provided visual content. #### 3. Scene and Activity Analysis: - **Scene Description:** The scene consists of a single document page that appears to be part of educational material or lecture notes regarding UNIX programs and signals. - **Activities:** The content is focused on explaining concepts related to UNIX interactive programs and signals, with an emphasis on the categorization of UNIX programs. #### 4. Text Analysis: - **Extracted Text Segments:** - **Title and Header:** - "Chapter 5 Interactive Programs and Signals" - **Subheader:** - "Concepts Covered" - Keywords such as "Software tools, daemons, interactive programs," among others. - **Sections:** - **5.1 The Different Types of UNIX Programs:** - Outlines the classification of UNIX programs. - **5.1.1 Software Tools (Filters):** - Describes what software tools are and how they function. - **5.1.2 Daemons:** - Provides an explanation of daemons in the UNIX environment. - **Footer:** - Copyright and licensing information, noting the author ("Stewart Weiss") and the Creative Commons license. - **Content Insight:** - The text is pedagogical, aiming to educate readers about the function and classification of UNIX programs. Categories include software tools, daemons, and interactive user programs, with a detailed narrative on software tools and daemons. #### 8. Color Analysis: - **Color Composition:** - The text is presented in black and white, typical for printed lecture notes, ensuring clear readability and focus on the content. #### 9. Perspective and Composition: - **Perspective:** - The image is a straight-on shot of a document page. - **Composition:** - The document is well-organized with clear headings, subheadings, bullet points, and structured paragraphs. - The layout includes proper spacing, aiding in readability. #### 11. Metadata Analysis: - **Metadata Overview:** - The metadata provided in the footer indicates that the material is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License, with the content attributed to Stewart Weiss. #### 14. Ablaufprozesse (Process Flows): - **Process Flow Description:** - **Software Tools (Filters):** Describes the flow from receiving input to producing output, including the ability to connect with standard input and output streams. - **Daemons:** Describes the background process flow of daemons, which run independently of terminal sessions and can be event-driven or user-requested. #### 17. Prozessbeschreibungen (Process Descriptions): - **Detailed Descriptions:** - **Software Tools:** In-depth description of the process of handling input and output in a UNIX environment through standard pipes and tools like awk, cat, cut, etc. - **Daemons:** Detailed explanation of how daemons operate in the background, waiting for certain events or signals to provide services. #### Contextual Significance: - **Document Context:** - The document is a chapter from UNIX lecture notes intended to educate students or readers on interactive programs and signals within UNIX systems. - **Contribution:** - The image contributes significant theoretical and practical knowledge crucial for understanding how UNIX operates, especially for those studying computer science or related fields. By breaking down this provided visual content, we can understand its educational purpose, structure, and how it fits within broader UNIX instruction, focusing specifically on interactive programs and signals. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 2 Context: # UNIX Lecture Notes ## Prof. Stewart Weiss ### Chapter 5 Interactive Programs and Signals Because daemons must not be connected to a terminal, one of their first tasks is to close all open file descriptors (in particular, standard input, standard output, and standard error). They usually make their working directory the root of the file system. They then take additional steps to break their association with any shell or terminal, among which are leaving their process group and registering their intent to ignore all incoming signals. The concept of process groups will be discussed in Chapter 8. Signals are covered later in this chapter. Daemon process names typically end in 'd'. This is one way to identify a daemon process in the output of the `ps -ef` command; find the names ending in "d" such as `ftpd`, `httpd`, `lpd`, `sshd`, `syslogd`, and `telnetd`. Daemons will be covered thoroughly in Chapter 8. ### 5.1.3 Interactive User Programs Another category of programs are those that are tied to the user terminal in an inextricable way, because they customize the terminal for their own use. Programs that interact with the user through the terminal, such as text editors (`vi`, `nano`, or `emacs`), pagers (`more` and `less`), terminal-based administrative tools such as `top`, games (`snake`, `worm`, and `chess`), and terminal-based mail clients (`pine` and `mailx`), are tightly coupled to the terminal and must control its settings and attributes. They cannot use the standard input and output streams for communicating with the user because these lack the types of controls that a terminal has. These types of programs usually need to control: - whether or not characters are echoed, - the number of characters that are buffered, if any, - the movement of the cursor on the screen, - whether certain key presses should have their default meaning or have application-defined meaning, - whether timeouts should occur on input, - whether signals such as Ctrl-C should be ignored, queued, or handled immediately. We already saw how to control the state of the terminal using `stty` at the command level and the `tcgetattr()` and `tcsetattr()` functions at the programming level. Here we will explore the various modes into which we can put the terminal for the benefit of creating interactive programs. ### 5.2 Designing Interactive User Programs Most interactive user programs are event-driven or menu-driven, meaning that they perform some short task and then wait for user input to do the next task. All window-based applications are event-driven; they are idle, often blocked on input, while they wait for mouse, keyboard, or other events to be delivered to them by the window manager. Here we go through the steps that are necessary to design and develop an interactive, terminal-based application. We will begin by understanding the problem, and then we will go through successive stages of making a program more and more responsive to user inputs. --- This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Comprehensive Examination of the Provided Image #### Image 1 **1. Localization and Attribution:** - **Position:** There is only one image present in the document. - **Number:** Image 1 **2. Object Detection and Classification:** - **Objects Identified:** - Textual content - Header section with course information - Footer section with licensing information - Bullet points - Section headers **3. Scene and Activity Analysis:** - **Scene Description:** The image displays a page from a lecture note discussing "UNIX Lecture Notes" specifically about "Interactive Programs and Signals". The professor's name and institutional information (name blurred out in accordance with the guidelines) are present. - **Activities:** Describes programming concepts including daemon processes, interactive user program characteristics, and design considerations. **4. Text Analysis:** - **Detected Text:** - Title: "UNIX Lecture Notes" - Course Description: "Chapter 5 Interactive Programs and Signals" - Professor's Name: "Prof. Stewart Weiss" - Section headings: "5.1.3 Interactive User Programs" and "5.2 Designing Interactive User Programs" - Main body: Discusses the nature of daemons, naming conventions, and attributes of interactive user programs and how to design them. - Bullet Points: Enumerates specific characteristics that interactive programs need to control (e.g., character echo, buffer size, cursor movement). - Footer: Licensing information stating rights and usage under the Creative Commons Attribution-ShareAlike 4.0 International License. **6. Product Analysis:** - **Depicted Document:** The document is an educational text likely used for teaching UNIX system programming. It includes theoretical knowledge and practical considerations for designing interactive user programs. **7. Anomaly Detection:** - No noticeable anomalies were detected in the structure or content of the image. **8. Color Analysis:** - **Color Composition:** - **Dominant Colors:** White and black due to the standard text on a white background. - **Impact:** The use of black text on a white background provides high readability and emphasizes a clean, professional look. **9. Perspective and Composition:** - **Perspective:** The image is taken from a direct, front-facing, bird’s-eye view commonly used for document scanning. - **Composition:** - The header includes the title and professor’s information. - The main body consists of structured paragraphs and bullet points. - Section headings are bolded to stand out. - The footer contains licensing information. **10. Contextual Significance:** - **Overall Document:** As part of UNIX lecture notes, this page contributes by teaching students about the internal mechanisms and considerations for writing interactive programs, focusing on the behavior and control of system processes. - **Contributions to Overall Message:** This image educates on specific technical aspects of important programming practices within UNIX systems, with a focus on user interactivity and background process management. **11. Metadata Analysis:** - **Image Metadata:** Not available from the provided image. **13. Graph Numbers:** - No graphs are present on the page for analysis. **Tables:** - No tables are included in the image for analysis. Overall, this document page offers detailed educational content for students or learners interested in UNIX system programming, particularly regarding interactive programs and process signals, enhancing their understanding through clear, structured information. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 3 Context: # UNIX Lecture Notes ## Prof. Stewart Weiss ### Chapter 5 Interactive Programs and Signals #### 5.2.1 Two Different Paradigms Consider the kind of terminal-based program in which the program repeatedly prompts the user for input and takes an action accordingly. One of the user responses such programs expect is some type of quit signal, which typically breaks the loop and ends the program. This can be modeled by a control structure such as the following: ``` while (true) { prompt the user to do something wait for the user to respond if user’s response is to quit break the loop and quit handle the response } ``` The input part of this loop usually results in the process being blocked on input, but it does not have to be designed this way. It might look like: ``` while (true) { prompt the user to do something if the user responded handle the response otherwise do other things } ``` In this paradigm, the program checks whether there is input and if there is, responds to it, and if not, it does something else. This approach requires the ability to check if there is input without blocking while waiting for it. In short, it requires a type of input operation known as non-blocking input, which will be discussed below. Regardless of which input method is used, programs such as video games, ATM machines, and text editors respond immediately to user key presses, rather than waiting for the Enter key to be pressed. They run in non-canonical mode, so they do not buffer input characters. Usually, they do not echo the input characters when these characters behave like function keys. Also, they usually ignore illegal key presses. Thus, one task in designing interactive programs is to determine how to control the state of the terminal in this way. But this is not enough. There is a big difference between a video game and a text editor, having to do with their relationship to time. We can distinguish between two kinds of interactive programs: those whose state is independent of time, and those whose state depends upon time. Any program that animates, in any way, is time-dependent; its state changes as a function of time. Programs that terminate or advance to a different state if the user does not respond within a certain amount of time are also time-dependent, because their state changes as a function of time. Video games are time-dependent. In contrast, a text editor is usually time-independent; it has no time-outs and no animation of any kind. Programming with time is more complex than programming in a time-independent way because it requires knowledge of several different parts of the kernel API. Before we tackle this problem, we will explore a simpler one, namely how to write a text editor. 1. I think about vi for example, and how it behaves in Command mode; you type `i` and it moves the cursor without displaying the letter, or more, when you type a space character and it advances a screen’s worth of lines. --- This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Detailed Analysis of the Visual Content 1. **Localization and Attribution:** - **Image Position and Numbering:** - The document contains one image. - This image is identified as **Image 1**. 2. **Text Analysis:** - **Detected Text:** The image contains textual content formatted as lecture notes on UNIX programming. The detected text is as follows: ``` UNIX Lecture Notes Prof. Stewart Weiss Chapter 5 Interactive Programs and Signals 5.2.1 Two Different Paradigms Consider the kind of terminal-based program in which the program repeatedly prompts the user for input and takes an action accordingly. One of the user responses such programs expect is some type of quit signal, which typically breaks the loop and ends the program. This can be modeled by a control structure such as the following: while (true) { prompt the user to do something wait for the user to respond if user’s response is to quit break the loop and quit handle the response } The input part of this loop usually results in the process’s being blocked on input, but it does not have to be designed this way. It might look like while (true) { prompt the user to do something if the user responded handle the response otherwise do other things } In this paradigm, the program checks whether there is input and if there is, responds to it and, if not, it does something else. This approach requires the ability to check if there is input without blocking while waiting for it. In short, it requires a type of input operation known as non-blocking input, which will be discussed below. Regardless of which input method is used, programs such as video games, ATM machines, and text editors respond immediately to user key presses, rather than waiting for the Enter key to be pressed. They run in non-canonical mode, so they do not buffer input characters. Usually, they do not echo the input characters when these characters behave like function keys 1. Also, they usually ignore illegal key presses. Thus, one task in designing interactive programs is to determine how to control the state of the terminal in this way. But this is not enough. There is a big difference between a video game and a text editor, having to do with their relationship to time. We can distinguish between two kinds of interactive programs: those whose state is independent of time, and those whose state depends upon time. Any program that animates, in any way, is time-dependent; its state changes as a function of time. Programs that terminate or advance to a different state if the user does not respond within a certain amount of time are also time-dependent, because their state changes as a function of time. Video games are time-dependent. In contrast, a text editor is usually time-independent; it has no time-outs and no animation of any kind. Programming with time is more complex than programming in a time-independent way because it requires knowledge of several different parts of the kernel API. Before we tackle this problem, we will explore a simpler one, namely how to write a text editor. 1 Think about vi for example, and how it behaves in Command mode: you type a `j’ äand it moves the cursor without displaying the letter, or more, when you type a space character and it advances a screen’s worth of lines. This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution- ShareAlike 4.0 International License. 3 ``` - **Content Analysis:** - The document is discussing two paradigms in the context of interactive terminal-based UNIX programs. - It presents examples of control structures that handle user input and describe how these structures can be designed to manage program flow. - The text explains the concept of non-blocking input operations and the differences between time-dependent and time-independent programs. 4. **Code Blocks:** - **Code Snippet 1:** ``` while (true) { prompt the user to do something wait for the user to respond if user’s response is to quit break the loop and quit handle the response } ``` - **Explanation:** - This snippet represents a typical loop for prompting user action. - It waits for the user to respond and processes their input, breaking the loop if the user chooses to quit. - **Code Snippet 2:** ``` while (true) { prompt the user to do something if the user responded handle the response otherwise do other things } ``` - **Explanation:** - This snippet shows an alternative approach where the program checks if the user has responded and acts accordingly without blocking. ### Summary: The document page focuses on explaining different paradigms of handling user input in UNIX terminal-based programs, demonstrating various control structures and explaining the concept of non-blocking input and time-dependency in programming. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 4 Context: # UNIX Lecture Notes **Prof. Stewart Weiss** **Chapter 5 Interactive Programs and Signals** ## 5.2.2 A Simple Text Editor: simplevi The `vi` text editor is a very complicated piece of software, but we can create an extremely stripped-down version of it and still learn quite a bit in the process. We will call our editor `simplevi`. This simple editor will allow the user to insert characters anywhere in a text document, but it will not provide a means of deleting characters. This is just a minor extension to the program. Also, it will not open an existing text file, but will instead create a new file each time it is invoked. Adding a feature to open a file does not provide much more insight into the interactive design of the program, but it does make the program larger. As much as possible, the features that `simplevi` does implement will have the same semantics as those in `vi`. ### 5.2.2.1 Features of simplevi The `simplevi` program allows a user to create a file in a manner similar to `vi`. It has only an insert command and does not presently support deletion. Like `vi`, it is always in exactly one of three possible modes: - input mode - command mode - last_line mode Each mode is now described briefly. #### Command Mode The initial mode is command mode. In `command_mode` one can enter the following keystrokes/commands: | Key | Semantics | |----------------------|-------------------------------------------------------------------| | `i` | changes to input mode. | | `.` | changes to lastline mode. | | `h`, backspace, or left-arrow key | moves left until the first character in the text line. If a line wraps, it follows the wrapped text backwards. | | `1`, spacebar, or right-arrow key | moves right until the last character in the text line. If a line wraps, it follows the wrapped text forwards. | | `x` or up-arrow key | moves to the text line above, to the same position relative to the start of the text line, unless that text line is shorter than the current one, in which case it is placed at the end of the text line. Scrolling takes place as necessary. | | `j` or down-arrow key | moves to the next text line below, using the same rule as for the up arrow when deciding the horizontal position. Scrolling takes place as necessary. | | `Ctrl-D` | Does nothing except display the message that it was pressed. | | `Ctrl-C` | Does nothing except display the message that it was pressed. | | `Ctrl-H` | Can be used to display a help screen, but at present just shows a one-line text message. | This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Image Analysis 1. **Localization and Attribution:** - There is only one image on the page, which we will refer to as **Image 1**. 2. **Object Detection and Classification:** - **Text:** The image contains a large amount of text divided into headings, paragraphs, bullet points, and a table. - **Table:** The image includes a table with two columns labeled "Key" and "Semantics." 3. **Scene and Activity Analysis:** - **Scene:** The image depicts a page from a set of lecture notes for a UNIX course. It appears to be an instructional text on a simplified text editor called "simplevi." - **Activities:** The text describes the functionalities and features of the simplified text editor, along with key commands and their semantics. 4. **Text Analysis:** - **Detected Text:** - **Headers and Subheaders:** - "UNIX Lecture Notes" - "Chapter 5 Interactive Programs and Signals" - "5.2.2 A Simple Text Editor: simplevi" - "5.2.1 Features of simplevi" - "Command Mode" - **Paragraphs:** - Describes the text editor "simplevi" and its basic functionality, contexts for use, and the features it does or does not include. - **Bulleted List:** - Lists the modes available in simplevi: input mode, command mode, and last_line mode. - **Table:** - Details the keystrokes and their corresponding semantics within Command Mode: - `i`: changes to input mode. - `:`: changes to lastline mode. - `h, backspace, or left-arrow key`: moves left until the first character in the text line. If a line wraps, it follows the wrapped text backward. - And many more similar command keys with their respective functions. - **Content Analysis:** - The text serves as an educational resource on a simple text editor, illustrating the basics of its use. This context is valuable for students or learners who are being introduced to text editors in UNIX environments. 5. **Diagram and Chart Analysis:** - There are no diagrams or charts in the image. 6. **Product Analysis:** - No products are depicted in this image. 7. **Anomaly Detection:** - There are no noticeable anomalies or unusual elements within the image. 8. **Color Analysis:** - The image is primarily in grayscale (black text on a white background) with some headings in bold for emphasis. The use of a grayscale color scheme is standard for textual or instructional materials and ensures readability. 9. **Perspective and Composition:** - **Perspective:** The image seems to be a direct scan or screenshot of a page from a document, presented in a straight, front-facing view. - **Composition:** The text is neatly organized with clear headings, subheadings, paragraphs, and a table. The table is centrally located and well-structured for easy reading and understanding. 10. **Contextual Significance:** - **Role in Document:** The image appears to be part of a chapter from UNIX lecture notes. It contributes instructional information on a command-line text editing program, which is relevant knowledge for those studying UNIX systems. - **Contribution:** It provides detailed instructions and commands, aiding learners in understanding and using the "simplevi" text editor effectively. 11. **Metadata Analysis:** - No metadata is available within the image itself. 12. **Graph and Trend Analysis:** - There are no graphs in the image. 13. **Graph Numbers:** - No graphs are included, hence no data points to list. ### Additional Aspects - **Ablaufprozesse (Process Flows):** - Process flows are depicted through the sequence of commands and their effects in the "Command Mode" section. - **Prozessbeschreibungen (Process Descriptions):** - The process of using the simplevi text editor is described, including switching between different modes and executing various commands. - **Typen Bezeichnung (Type Designations):** - Types or categories are designated through the various modes of the text editor and the associated keyboard commands. - **Trend and Interpretation:** - Trends are inherent in the design and operation of the text editor, showcasing a simplified structure that focuses on essential command-line operations. - **Tables:** - The table concisely lists command keys and their functions, providing an essential quick reference for users. This comprehensive examination of the image covers the key aspects you've requested. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 5 Context: # UNIX Lecture Notes **Prof. Stewart Weiss** **Chapter 5 Interactive Programs and Signals** ## Notes - Navigation keys use rules similar, but not identical, to vi's rules. The only difference is that vi remembers the last horizontal movement, and when a vertical movement is indicated, the cursor moves to the position in the new text line that is the same as the last remembered horizontal movement. - Because there is only an insert operation, and not an append operation, the cursor is allowed to be one position to the right of the rightmost character in a line. ### Lastline Mode The user can quit the editor in lastline mode, and/or save the current buffer contents. The allowed commands are a subset of the commands possible in vi, but the syntax mimics vi. Specifically, in lastline mode one can enter: - `w filename` to save the buffer to a file named `filename` - `vq filename` to save the buffer to a file named `filename` and quit after - `q` to quit without saving. It does not warn that the buffer is not saved. Any amount of white space is allowed before the command and between the command and the filename. Any characters other than these generate an error message and terminate lastline mode. Filenames can be any combination of alphanumeric characters and underscores. Punctuation and whitespace are not allowed. Typing a command such as: ``` :w q ``` creates a file named `q`, as would happen in vi. Letting `S` denote a space character, `F` denote a valid filename character, parentheses for grouping, and `|` for alternation, the language of acceptable newline-terminated strings in lastline mode is: ``` S*(w|S*q)S*F*S* | S* qS* ``` ### Input Mode There is only an insert operation, which inserts at the cursor position. The backspace is not implemented. Typing a backspace has no effect. Typing characters other than graphical characters (those found on the keyboard) has unspecified behavior. Graphical characters are inserted to the left of the cursor. Lines wrap as necessary, and the screen scrolls as needed as well. ## 5.2.2.2 Program Design It would be much easier to write this program if we used the NCurses library, but as we have not yet covered the NCurses API, we will do it the hard way, the way it was done before NCurses existed. This will give you an appreciation of NCurses when we cover it in the next chapter. Here, we will use the ANSI escape sequences that we covered in Chapter 1. The design challenge in writing this simple text editor is synchronizing the three major objects that the program must manage: This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Comprehensive Examination of the Provided Image --- #### 1. **Localization and Attribution:** - **Image Number:** The provided image can be identified and located as **Image 1**. #### 2. **Object Detection and Classification:** - The primary objects in **Image 1** include: - **Text Blocks:** These are the most notable elements in the image. - **Headers and Subheaders:** Specific sections with different font sizes and styles. #### 3. **Scene and Activity Analysis:** - **Scene Description:** The scene consists of a structured document page, most likely a lecture note or an instructional text. - **Activities:** - Reading and assimilating information about UNIX, specifically related to interactive programs and signals. - Understanding command usage, particularly of text editors like `vi` and command line operations. #### 4. **Text Analysis:** - **Detected Text:** - **Header:** "UNIX Lecture Notes" and "Chapter 5 Interactive Programs and Signals" - **Author Note:** "Prof. Stewart Weiss" - **Section Title:** "Notes" - Details about navigation keys, vi commands, and text editor modes. - **Subsection Title:** "Lastline Mode" - Commands such as `w filename`, `wq filename`, and `q`. - **Code Example:** ``` : w q ``` - **Subsection Title:** "Input Mode" - **Subsection Title:** "Program Design" - Discussion on writing programs with and without the NCurses library. - **Text Significance:** - The text primarily serves an instructional purpose, aimed at educating readers about Unix text editors, and related commands. - It explains operational aspects of the text editor `vi`, detailing command usage and modes of operation. - The emphasis on navigation keys and lastline mode commands are instructional elements, guiding users on practical usage. #### 9. **Perspective and Composition:** - **Perspective:** This image is taken from a straight-on perspective often used for documents to ensure clarity and readability. - **Composition:** - The document is composed in a structured format with headers, subheaders, bullet points, and code examples. - The header is bold and centered, indicating importance. - Subsections and bullet points are utilized for clarity and easy navigation through the content. - Code examples are displayed in a monospaced font, separated from the main text for emphasis. #### 12. **Graph and Trend Analysis:** - **Trends:** - The image does not demonstrate any graphs or visual data trends. Instead, the trend evident here is instruction-based text, progressively elaborating on the usage and functionality within a UNIX environment. #### 8. **Color Analysis:** - **Color Composition:** - The primary colors are black text on a white background, facilitating readability. - No use of color differentiation within the document body suggests a purely text-based instructional focus without the need for visual aids. #### 10. **Contextual Significance:** - The image contributes to the overarching theme of UNIX educational notes, specifically tailored to teaching interactive program commands and signal handling in UNIX. - It supports the document's educational purpose, providing detailed explanations of specific commands and their utilities in textual and programmatic contexts. #### 13. **Graph Numbers:** - Not applicable as there are no graphs present in **Image 1**. ### Summary **Image 1** is a page from UNIX lecture notes focusing on interactive programs and signal handling. It includes detailed explanations of command usage in `vi`, outlining lastline mode commands and operational modes, with an emphasis on practical command-line applications. The image uses clean composition and structured layout to impart clear instructional content. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 6 Context: # UNIX Lecture Notes ## Chapter 5 Interactive Programs and Signals ### Prof. Stewart Weiss - a behind-the-scenes text buffer, - the visible screen, and - the cursor. ## The Buffer Object The text that the user types must be stored in a buffer of some kind. There are many possible ways to organize this buffer, with time-space trade-offs associated to each. A reasonable solution would be to create an array of pointers to the individual lines of the buffer, each of which would be a dynamically allocated fixed-size array. The array of pointers could be replaced by a doubly-linked list of pointers with added programming complexity. The arrays holding the lines of text could be started smaller and reallocated to larger sizes as the lines grow in size. These details are not important to us now, as performance is less of a concern than understanding the principles. Therefore, we take an even simpler approach: the text buffer is just a large linear array of characters. Because vi is a line-oriented editor and cursor movement follows text lines, it is convenient for the text buffer to keep track of the starts of each line as well as its length, as well as the number of lines and the line in which the cursor is currently located and the cursor's offset from the beginning of that line. It will also be convenient for the buffer to store the cursor’s position as the offset from the beginning of the linear array itself. Thus there is some redundancy in the representation, and the buffer object must have functions to synchronize all members. The buffer object is defined as follows: ```c typedef struct _buffer { char text[BUFSIZ]; /* length of text lines, including newline characters */ int line_len[MAXLINES]; /* starts of each line */ int line_start[MAXLINES]; /* number of text lines in buffer. This includes lines that have not yet been terminated with a newline character. It is the number of newline characters + 1 if the last character in the buffer is not a newline. */ int num_lines; /* index of cursor in text buffer */ int size; /* total chars in buffer */ int cur_line; /* current text line, not screen line */ int index_in_cur_line; /* index in current line of cursor */ } Buffer; ``` The BUFSIZ constant is a system constant, and MAXLINES is defined in the application header file. ## The Window Object The window is a display object. The program needs to find its dimensions on start up and store them in the window object. Because the buffer contents may be larger than can fit in the window, at any given time, there is a set of text lines that is visible in the window. This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Analysis of the Visual Content #### 1. Localization and Attribution - **Image Position:** There is a single page and hence a single image in the document. - **Image Number:** Image 1. #### 4. Text Analysis - **Detected Text:** - **Header:** - **Title:** UNIX Lecture Notes - **Subtitle:** Chapter 5 Interactive Programs and Signals - **Author:** Prof. Stewart Weiss - **Bulleted Points Near Top:** - ● a behind-the-scenes text buffer, - ● the visible screen, and - ● the cursor. - **Main Content:** - **Title:** The Buffer Object - **Body Text:** The text describes the components and functionalities necessary for storing text input by users. It discusses various methods of organizing text buffers and details the specifics of a simple linear array approach. Key points include the use of indices to manage lines of text and the definition of a buffer struct in C programming language. - **Code Example:** ```C typedef struct _buffer { char text[BUFSIZ]; /* lengths of text lines, including newline characters */ int line_len[MAXLINES]; /* lengths of each line */ int line_start[MAXLINES]; /* starts of each line */ int num_lines; /* number of text lines in buffer */ int index; /* index of cursor in text buffer */ int size; /* total chars in buffer */ int cur_line; /* current text line, not screen line */ int index_in_cur_line; /* index in current line of cursor */ } Buffer; ``` - **Note:** The `BUFSIZ` constant is a system constant, and `MAXLINES` is defined in the application header file. - **Text Title:** - **Title:** The Window Object - **Body Text:** This section explains that the window is a display object and discusses the program's need to maintain and store the dimensions of the window upon startup. - **Footer:** - **Copyright Notice:** This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. #### 5. Diagram and Chart Analysis - **Code Snippet Analysis:** - The provided code snippet is a C struct named `_buffer` designed to store text data for a text editor or similar application. - **Key Elements of the Struct:** - `char text[BUFSIZ]`: Holds the text content. - `int line_len[MAXLINES]`: Keeps track of the length of each line. - `int line_start[MAXLINES]`: Marks the start of each line. - `int num_lines`: Number of lines in the buffer. - `int index`: The cursor's position within the buffer. - `int size`: Total number of characters in the buffer. - `int cur_line`: The line the cursor is currently on. - `int index_in_cur_line`: The cursor's position within the current line. #### 8. Color Analysis - **Color Composition:** The image appears to be primarily black and white, typical for text documents. There is no use of color that impacts perception significantly. #### 9. Perspective and Composition - **Perspective:** The document is viewed from a standard front-on angle, as one would view a printed page or a document on a screen. - **Composition:** - The text is neatly formatted in blocks with appropriate use of headings, sub-headings, and code blocks. - Bullet points are used at the start to list key components. #### 10. Contextual Significance - **Document Context:** This image is clearly part of an educational material on UNIX systems, specifically discussing interactive programming and signals. - **Contribution to Overall Theme:** The detailed explanation of buffer and window objects contributes to a deeper understanding of text handling in UNIX environments, which is likely instrumental for understanding and writing interactive programs in this context. ### Conclusion The document appears to be a well-organized lecture note, focusing on the implementation and functionality of text buffers and window objects within the context of UNIX interactive programming and signals. It provides clear textual explanations complemented by practical code examples to illustrate the concepts discussed. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 7 Context: # UNIX Lecture Notes ## Prof. Stewart Weiss ### Chapter 5 Interactive Programs and Signals and this is a subsequence of the lines in the buffer. The smallest index of this sequence is stored as a member of the window to facilitate redrawing the buffer during scrolling operations and insertions. The last line index is recalculated rather than storing it. It could also be stored to save running time. Finally, because the erase character is a terminal setting, its value is stored within the window object (a window is really a terminal), and will be determined at program startup by querying the terminal driver. The window object is defined by: ```c typedef struct _window { unsigned short rows; unsigned short cols; int line_at_top; char erase_char; } Window; ``` The window is divided into two regions: the line at the bottom of the screen and everything above it. The bottom line is reserved by the application for writing status messages and for lastline mode, which is explained below. The text is never allowed to be written onto the window's last line. ## The Cursor The cursor is simple: it is a structure with a row and column index, representing a position in a two-dimensional array whose upper-left corner is position (0,0). The cursor is defined by: ```c typedef struct _cursor { int r; int c; } Cursor; ``` Although the cursor position is in zero-based two-dimensional coordinates, the ANSI escape sequences use a 1-based set of coordinates: the upper-left corner of the screen is (1,1). Therefore, the cursor functions must account for this difference. For example, the function to move the cursor to a new row and column position is as follows: ```c void moveto(int line, int column) { char seq_str[20]; sprintf(seq_str, "\033[%d;%dH", line + 1, column + 1); write(1, seq_str, strlen(seq_str)); } ``` Some of the cursor functions are relatively simple. When `simplevi` is in input mode and a character is typed on the keyboard, three things must happen: - The character must be inserted into the text buffer; This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Analysis Results #### 1. **Localization and Attribution:** - **Image 1:** Sole image on the page located at the center. #### 2. **Object Detection and Classification:** - Objects: The image contains text, which includes programming code, descriptions, and a header. #### 4. **Text Analysis:** - **Header Text:** - "UNIX Lecture Notes," "Chapter 5 Interactive Programs and Signals," "Prof. Stewart Weiss." - Provides context, indicating this is a section from a UNIX programming lecture. - **Body Text:** - Description of window and cursor in a UNIX system. - `typedef struct _window` and `typedef struct _cursor`: Defines structures in C programming. - Description of the `moveto` function implemented in C for cursor movement. - Explanation of operations when `simplevi` is in input mode and a character is typed. - **Significance:** - The text gives educational content, explaining practical implementations in UNIX programming. It is valuable for students or readers learning about terminal control and cursor management in C. #### 9. **Perspective and Composition:** - **Perspective:** - The image presents a straightforward, two-dimensional view of a text document, oriented vertically for easy reading. - **Composition:** - Structured hierarchically: header at the top, followed by different sections (`The Window`, `The Cursor`), and code snippets are in-line with explanations. ### Conclusion The image represents a page from UNIX lecture notes focusing on the topics of windows and cursors within interactive programs and signals. It provides theoretical explanations supported by practical C code examples. The document is instructional and intended for educational purposes in the context of learning UNIX programming. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 8 Context: # UNIX Lecture Notes ## Prof. Stewart Weiss ### Chapter 5 Interactive Programs and Signals - The cursor must be advanced to the next insertion point (because echo is controlled by the application, not by the terminal driver), and - The window must be redrawn to account for the inserted character. The function to advance the cursor is in the listing below. ### Listing 5.1: advance_cursor ```c void advance_cursor( Cursor *cursor, Window win, char ch ) { if ( ch == '\n' ) { cursor->r++; cursor->c = 0; } else { cursor->c++; if ( cursor->c == win.cols ) { /* wrap the line */ cursor->c = 0; cursor->r++; } } } ``` The function checks which character was typed. If a newline, it advances the cursor to the next line at column 0, and if not, it checks whether it has to account for wrapping the line; if the cursor is at the right margin, it also advances to the next line, column 0. ### Inserting a Character When the program is in input mode and the user enters a character to be inserted into the buffer, four actions must be taken: 1. Physically inserting the character into the buffer, provided no overflow occurs, 2. Advancing the cursor to the next logical position in the window, 3. Redrawing the buffer contents to the screen, which may cause the cursor's logical position to change, and 4. Physically moving the cursor to the final screen position. The first step is implemented by the `insert()` function in Listing 5.2 below. ### Listing 5.2: insert ```c int insert( Buffer *buf, Window win, char c ) { int i; if ( ( c == '\n' ) && ( MAXLINES == buf->num_lines ) ) return OUT_OF_LINES; else if ( buf->size == BUFSIZ ) return OUT_OF_MEM; } ``` This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Image Analysis #### Image Description: The image consists of an excerpt from a UNIX Lecture Note which outlines functions and actions related to cursor movement in the context of interactive programs and signals. ### Localization and Attribution: - **Location on Page:** The entire content is contained in a single page. - **Number of Images:** Image 1 ### Text Analysis (Image 1): #### Text Content: 1. **Title and Headers:** 1. **Main Heading:** "UNIX Lecture Notes" 2. **Sub-heading:** "Chapter 5 Interactive Programs and Signals" 3. **Author:** "Prof. Stewart Weiss" 4. **Section / Sub-Section:** "Inserting a Character" 2. **Key Points:** - The cursor must be advanced to the next insertion point and the window must be redrawn to account for the inserted character. - The function to advance the cursor is detailed in Listing 5.1. - The function to insert characters into a buffer is detailed in Listing 5.2. 3. **Code Listings:** - **Listing 5.1: advance_cursor** ```c void advance_cursor( Cursor *cursor, Window win, char ch) { if (ch == '\n' ) { cursor->r++; cursor->c = 0; } else { cursor->c++; if ( cursor->c == win.cols ) { /* wrap the line */ cursor->c = 0; cursor->r++; } } } ``` - **Explanation for Listing 5.1:** - The `advance_cursor` function moves the cursor either to the next line or to the next column within the same line. It also handles line-wrapping when the cursor reaches the end of a line. - **Listing 5.2: insert()** ```c int insert( Buffer *buf, Window win, char c) { int i; if (( c == '\n' ) && ( MAXLNES == buf->num_lines ) ) return OUT_OF_LINES; else if ( buf->size == BUFSIZ ) return OUT_OF_MEM; } ``` - **Explanation for Listing 5.2:** - The `insert` function inserts a character into a buffer, flags overflow conditions (out of lines and out of memory), and handles cursor positioning accordingly. 4. **Descriptive Sections:** - The function `advance_cursor` mainly advances the cursor by either moving it to the next insertion point or wrapping it to the next line if necessary. - The `insert` function detailed includes buffer overflow checks and memory management during character insertion. ### Key Insights: - **Advance Cursor Function:** The key function, `advance_cursor`, deals with the logic required to move the cursor based on the character input ('\n' for newline, else move right). - **Insert Function:** The `insert` function focuses on buffer management and depicts potential edge cases like running out of lines or buffer size in handling character insertions. ### Code and Steps Explained in Context: - The image provides a detailed explanation and code snippets for handling cursor and buffer management in UNIX terminal applications. - The code facilitates understanding the low-level operations required for interactive terminal programs. ### Conclusion: The image from the UNIX Lecture Note clarifies essential processes related to handling cursor movements and character insertions in interactive programs. The explanations and code listings (5.1 and 5.2) offer valuable insights into effective buffer and cursor management in a terminal using C code. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 9 Context: # UNIX Lecture Notes ## Prof. Stewart Weiss ### Chapter 5: Interactive Programs and Signals ```c if (c == buf->erase_char) { /* Not implemented -- just ignore it */ return UNHANDLEDCHAR; } for (i = buf->size; i > buf->index; --i) { buf->text[i] = buf->text[i - 1]; buf->text[buf->index] = c; buf->size++; buf->index++; buf->line_len[buf->cur_line]++; } /* The first character sets line count to 1 */ if (buf->size == 1) buf->num_lines++; /* If (c == '\n') { */ /* Save the length of the line being split by the newline */ int temp = buf->line_len[buf->cur_line]; /* The new length of current line is the current index position + 1. */ buf->line_len[buf->cur_line] = buf->index_in_cur_line + 1; /* Increase number of lines */ buf->num_lines++; /* Shift all line starts and lengths upwards in the array, but */ add 1 to the line starts since they are 1 character further than before because of the new newline. Do this from the last line down to cur_line + 1, which is the line just after the split line. */ for (i = buf->num_lines - 1; i > buf->cur_line + 1; --i) { buf->line_len[i] = buf->line_len[i - 1]; buf->line_start[i] = buf->line_start[i - 1] + 1; } /* Set the start of the new line created here. It is the sum of the start of cur line plus the length of cur_line. */ buf->line_start[buf->cur_line + 1] = buf->line_start[buf->cur_line] + buf->line_len[buf->cur_line]; /* Advance to new line */ buf->cur_line++; /* The length of the newly created line is the number of characters that were to the right of the current index position. */ buf->line_len[buf->cur_line] = temp - buf->line_len[buf->cur_line - 1]; buf->index_in_cur_line = 0; } else if (isprint(c)) { /* Non-newline character */ buf->index_in_cur_line++; // advance index in line /* Increment all line starts after this line */ for (i = buf->cur_line + 1; i < buf->num_lines; i++) { buf->line_start[i]++; } buf->line_start[buf->cur_line]++; } ``` This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Image Analysis: #### 1. **Localization and Attribution:** - **Image 1**: The presented image occupies the full page. #### 2. **Object Detection and Classification:** - **Objects Identified**: - Text blocks - Logos - Headers and footers #### 3. **Scene and Activity Analysis:** - **Scene Description**: The image is a digital page of lecture notes titled "UNIX Lecture Notes" by Prof. Stewart Weiss. The notes focus on Chapter 5, which deals with "Interactive Programs and Signals." The main activity here involves the textual presentation of a code snippet and explanatory comments. #### 4. **Text Analysis:** - **Extracted Text**: The page features a mixture of code in C and comments. ``` if ( c == win.erase_char ) { /* Not implemented — just ignore it */ return UNHANDLEDCHAR; } for ( i = buf->size; i > buf->index; i— ) buf->text[i] = buf->text[i-1]; buf->text[buf->index] = c; buf->size++; buf->index++; buf->line_len[buf->cur_line]++; ... ``` - **Content Analysis**: - The text features a code snippet in C language, with operations related to managing and editing a buffer. - Comments within the code provide insight into the logic and the purpose of each operation, such as handling special characters, adjusting the buffer array, managing line length, and handling newline characters. - This is likely part of a lecture on programming, specifically dealing with buffer handling in text processing. #### 8. **Color Analysis:** - **Dominant Colors**: - The image is predominantly monochromatic, consisting mainly of black text on a white background. - The exception is the blue logo at the top-right corner. #### 9. **Perspective and Composition:** - **Perspective**: The image is a direct capture of a digital lecture note page, viewed from a straight-on perspective, simulating the experience of reading a printed page or a PDF. - **Composition**: The text is well-structured with clear headings and subheadings. The code is interspersed with comments to facilitate understanding. The placement of the logos and headers/footers lend a formal academic presentation. #### 10. **Contextual Significance:** - **Overall Document Context**: - This image is part of a larger set of lecture notes intended for educational purposes. - The specific details and comments within the code indicate it is a teaching tool aimed at illustrating buffer management in UNIX programming. ### Summary: The image represents a page from a lecture note on UNIX programming. It includes a code snippet with meticulous comments designed to teach students about handling text buffers and special characters in C. The page composition is clear and pedagogically structured, enhancing the learning experience. The monochromatic color scheme focuses attention on the textual and logical content, while the straight-on perspective mimics a traditional reading format. This page is part of a broader academic resource by Prof. Stewart Weiss, aimed at advancing students' understanding of interactive programming and signals in UNIX. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 10 Context: # UNIX Lecture Notes ## Prof. Stewart Weiss ### Chapter 5: Interactive Programs and Signals ```c else return UNHANDLEDCHAR; return 0; ``` Skipping some of the details, this function basically shifts all characters upwards in the buffer and adjusts the starts of lines for all lines after the current line. It inserts the new character at the index position, increments the buffer size, the index position, and the length of the current line. It has to determine whether the inserted character is a newline character. If so, it has to split the line and adjust the line starts and line lengths arrays. If not, it checks if it is a printable character, and if so, adjusts the index in the current line and lines accordingly. The second step, advancing the cursor, is shown in Listing 5.1. The third step, redrawing the buffer, is handled by the `redraw_buffer()` function, which is found in Listing 5.3 below. ### Listing 5.3: `redraw_buffer()` ```c void redraw_buffer(Buffer buffer, Window swin, Cursor *curs) { int i; int lastline; int lastchar; int firstchar; int line_of_cursor; /* If the current position in the buffer, buffer.index, is not within the visible lines of the window, the window must be shifted. The shift might be up or down, depending on whether index is above or below the window. */ // We first need to get the number of the line containing pos. Then we // check whether that line is between win.line_at_top and lastline. // We need to calculate the difference and shift win.line_at_top that // difference, and recalculate lastline, after which we can draw the // buffer. /* Compute the last visible complete text line in the buffer */ get_lastline_in_win(&buffer, &swin, &lastline); /* Get the index of the text line containing the insertion position */ line_of_cursor = line_in_buffer(&buffer, swin, buffer.index); /* Check if the window needs to be scrolled */ if (line_of_cursor < swin.line_at_top) { lastline = (swin.line_at_top - line_of_cursor); curs->r = (swin.line_at_top - line_of_cursor); win.line_at_top = line_of_cursor; } else if (line_of_cursor > lastline) { curs->r = (line_of_cursor - lastline); lastline = line_of_cursor; } /* Get the first and last characters of the visible screen. The lastchar is the index of the last character that can appear in the */ } ``` This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Detailed Analysis of the Visual Content #### 1. Localization and Attribution - **Image Position:** The visual content consists of a single image. - **Image Number:** Image 1 #### 4. Text Analysis **Detected Text:** - The text in the image is an excerpt from a document titled "UNIX Lecture Notes" by Prof. Stewart Weiss. It is from Chapter 5: Interactive Programs and Signals. - The text primarily consists of a detailed explanation and a code snippet. **Content of the Text:** - **Explanation:** The text describes a function responsible for managing a buffer. This function shifts characters upwards and adjusts the start of lines in the buffer after a particular position. It includes details about inserting and checking characters and how the buffer handles newline and printable characters. Additionally, it mentions adjusting the index in current lines accordingly. - **Code Snippet:** A function called `redraw_buffer()` is shared. This function is responsible for managing the buffer's visibility within a window and ensuring proper scrolling. The code snippet highlights several steps: - **Defining Variables:** Variables such as `lastline`, `lastchar`, `firstchar`, and `line_of_cursor` are declared. - **Shifting the Window:** The function checks if the buffer's current position is within the visible lines of the window and handles shifting the window accordingly. - **Calculating Positions:** It computes the complete visible line and the index of the insertion position. Based on these indices, it adjusts the visible part of the buffer within the window. **Significance:** The detailed explanation and code snippet provide insights into handling buffer visibility and scrolling within text editors or similar interfaces. This is crucial for understanding underlying OS or software mechanisms. #### 14. Ablauff Prozesse (Process Flows) **Described Process Flows:** - **Buffer Adjustment and Character Shifts:** The first part of the text explains how characters in a buffer are managed, including details on moving positions and adjusting line starts based on new characters. - **Redrawing Buffer:** When the cursor moves and the visible part of the buffer needs adjustment, the function `redraw_buffer()` calculates necessary shifts and updates the visible lines accordingly. #### 15. Prozessbeschreibungen (Process Descriptions) - **Buffer Management Process:** The process is described as handling character placements in a buffer, detecting newline or printable characters, and updating line starts accordingly. - **Redrawing Process:** Describes how to handle visible areas in a window, including shifting the window content up or down based on cursor movement and recalculating visible line positions. #### 20. Typen Bezeichnung (Type Designations) - **Function and Variables:** Designations like `Buffer`, `Window`, and `Cursor` indicate specific types or categories of elements being interacted with in the function. They represent core components in managing the editor's buffer. #### 23. Trend and Interpretation - **Trend:** The content emphasizes robust buffer management and efficient handling of the visibility of characters within an interface. This is reflective of good programming practices in software development. - **Interpretation:** This thorough handling of buffer and cursor movement is essential for creating smooth and user-friendly text-editing experiences, ensuring that any updates or changes within the buffer are accurately reflected in the user’s view. Overall, the visual content meticulously explains and demonstrates practical aspects of buffer management in Unix's interactive programming, emphasizing efficiency and accuracy in text handling. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 11 Context: # UNIX Lecture Notes ## Prof. Stewart Weiss ### Chapter 5 Interactive Programs and Signals ```c window -- the last character in the last visible line. The first char is the start of the line at the top of the screen. */ lastchar = buffer.line_start[la_stline] + buffer.line_len[lastline]; firstchar = buffer.line_start[win->line_at_top]; /* Prepare to redraw the window. First clear the screen. */ write(1, CLEAR_SCREEN, CLEAR_SCREEN); /* Do the redraw */ move(0, 0); for (i = firstchar; i < lastchar; i++) write(1, &buffer.text[i], 1); } ``` The `redraw_buffer()` function could be more efficient. It is written for clarity. The problem is that the line containing the insertion might have grown long enough that it wrapped and part of it is no longer in the visible region of the window. It could also be that the inserted character is outside of the visible region because the cursor was at the right margin and the new character is in the next row of the screen. In these cases, the entire window must be redrawn because the starting line and ending line have changed. In other cases, it is not necessary to redraw the entire window, but for simplicity, the entire window is always redrawn. Redrawing the buffer requires determining whether the current line and the index of the cursor in that line are outside of the visible region. This is accomplished in a few steps: 1. Getting the index of the last visible line in the window. 2. Getting the index of the text line containing the insertion position. 3. Comparing the line of the insertion point to the top line and last line computed above, and if out of bounds, resetting the line at the top of the window so that the insertion point is visible. 4. Calculating the first and last characters of the buffer that delimit the visible characters in the window. 5. Moving to the upper left corner and redrawing all characters from the first to the last. ### Cursor Movement One of the primary challenges in managing the cursor is that, at any given time, the program must be able to map the cursor position to a position in the text buffer, and vice versa. The program will always make sure that it knows the current line and the current index in the line as the cursor moves around on the screen. In fact, the cursor movement operations will actually update these variables, and from those recalculate the cursor position. This needs explanation. Text lines may be longer than the screen width. When this happens, they wrap onto two or more lines. In `vi`, when the cursor is moved upward or downward, it “jumps” over the wrapped lines. In other words, it moves from one text line to another, not from one screen row to another. Our `simplevi` program emulates this behavior. Therefore, when the user presses an arrow key up or down, it will move to the preceding or following text line. To make this possible, it will increment or decrement the `cur_line` member of the buffer as needed, and possibly adjust the `index_in_cur_line` variable, as will be explained below. But this implies that the index member is no longer synchronized with This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Comprehensive Examination of the Attached Visual Content #### 1. Localization and Attribution - **Image Location**: There is a single page containing the visual content. - **Image Number**: Image 1. #### 2. Object Detection and Classification - **Objects**: - Text blocks. - Code snippet. - Headings. - **Classification**: - The text appears to be a lecture note or documentation. - The code snippet is a C-like programming language. #### 3. Scene and Activity Analysis - **Scene Description**: - The page is from a document titled "UNIX Lecture Notes". - The section appears to be from Chapter 5: "Interactive Programs and Signals" written by Prof. Stewart Weiss. - The primary focus is on the techniques of managing screen-based cursor movement and redrawing buffers in programming. - **Activities**: - Explanation and teaching of a specific function, `redraw_buffer()`. - Detailed steps on how to manage cursor movement in text buffer applications. #### 4. Text Analysis - **Main Text Content**: - The text provides an explanation of how the `redraw_buffer()` function works and checks various conditions to decide whether the entire window must be redrawn. - It includes steps to determine and manage the redraw of visible regions in a buffer. - There is a sub-section discussing challenges of cursor management and how a program should handle cursor movements across the text buffer. - **Code Snippet**: ```c window -- the last character in the last visible line. The first char is the start of the line at the top of the screen. */ lastchar = buffer.line_start[lastline] + buffer.line_len[lastline]; firstchar = buffer.line_start[win->line_at_top]; /* Prepare to redraw the window. First clear the screen. */ write(1,CLEAR_SCREEN, ICLEAR_SCREEN); /* Do the redraw */ moveto(0,0); for ( i = firstchar; i < lastchar; i++ ) write(1,&buffer.text[i], 1 ); ``` #### 6. Product Analysis - **Descriptions**: Not applicable as no specific products are depicted. #### 8. Color Analysis - **Color Composition**: - The page is predominantly in grayscale with black text on a white background. #### 9. Perspective and Composition - **Perspective**: - The perspective is a straight-on view of a document page. - **Composition**: - The layout is structured with headers, a code snippet, and explanatory paragraphs. - The primary header is located at the top. - The code snippet is boxed or visually separated from the explanatory text. #### 10. Contextual Significance - **Document Context**: - The document is educational, aiming to teach aspects of UNIX programming. - This page specifically focuses on how to handle screen refreshes and cursor movements, which are critical for rendering text in interactive programs. - **Contribution to the Message**: - It contributes to the overall understanding of handling text display and cursor positioning in Unix-based text editors or similar applications. #### 12. Graph and Trend Analysis - **Graph Numbers**: Not applicable as there are no graphs present. #### **Additional Aspects to Include:** - **Ablaufprozesse (Process Flows)**: - The text describes specific processes involved in redrawing buffer and managing cursor movements. - **Prozessbeschreibungen (Process Descriptions)**: - Detailed steps on determining whether the current line and the cursor are in the visible region of the window. - Steps to get and compare indices for redrawing purposes. #### Summary The attached content is a page from a UNIX lecture note, focusing on the process of redrawing buffers and cursor movement in text editing applications. It contains a code snippet explaining the redraw function, followed by a detailed text explanation addressing the challenges and steps involved in managing the cursor position and redrawing the visible text region. The page is formatted in grayscale with headers, code boxes, and structured paragraphs for clarity. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 12 Context: # UNIX Lecture Notes **Prof. Stewart Weiss** **Chapter 5: Interactive Programs and Signals** ## Listing 5.4: update_buffer_index() ```c void update_buffer_index( Buffer *buffer ) { int totalchars = 0; int i = 0; while ( i < buffer->cur_line ) { totalchars += buffer->line_len[i]; i++; } totalchars += buffer->index_in_cur_line; buffer->index = totalchars; } ``` Similarly, it is also necessary to get the cursor's actual window coordinates when all that is known are the current line number and the offset within that line. This is the case, for example, when the down arrow key is pressed and the cursor must be moved to a new text line. It is possible that the cursor would actually be below the last visible line. The only way to know this is to calculate its window coordinates and compare them to the bottom of the screen. The following function is our means of getting the cursor position. ## Listing 5.5: get_cursor_at() ```c void get_cursor_at( Buffer *buf, Window win, int index, int lineno, Cursor *curs ) { int total_lines_before = 0; int rows_in_current_textline = 0; int i; /* The first line is the one at the top of the window, whose index is win.line_at_top; initially 0 */ for ( i = win.line_at_top; i < lineno; i++ ) { if ( buf->line_len[i] < win.cols ) total_lines_before++; else total_lines_before += (int) ceil((double)buf->line_len[i]/win.cols); } rows_in_current_textline = index / win.cols; curs->y = total_lines_before + rows_in_current_textline; curs->x = index - rows_in_current_textline * win.cols; } ``` There are four movements that the program has to implement: up, down, left, and right. We show just one of these here. The complete program is in the appendix. The up and down movements are a bit more complex than left and right. When the down arrow key is pressed, or the `j` key, the cursor must be moved to the position in the text line below at the same offset relative to the beginning of the line, unless that line is shorter. This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Analysis of the Attached Visual Content #### 1. Localization and Attribution: - **Single Image**: The document features only one page with text and code snippets. #### 2. Object Detection and Classification: - **Detected Objects**: - Text paragraphs - Code blocks - Headers and footers #### 4. Text Analysis: - **Detected Text**: - **Header**: - Left: "UNIX Lecture Notes" - Center: "Chapter 5 Interactive Programs and Signals" - Right: "Prof. Stewart Weiss" - **Footer**: - Left: "This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License." - Page Number: "12" - **Main Text**: - The text explains a specific function `update_buffer_index()` which updates an index in a buffer only when an insertion or other operation is performed on the text. - It details a process to calculate the cursor’s actual window coordinates, important for getting the current line number and offset within that line. It also explains cursor movement implementations (up, down, left, right). - **Code Snippets**: - **Listing 5.4: `update_buffer_index()`** ```c void update_buffer_index( Buffer *buffer ) { int totalchars = 0; int i = 0; while ( i < buffer->cur_line ) { totalchars += buffer->line_len[i]; i++; } totalchars += buffer->index_in_cur_line; buffer->index = totalchars; } ``` - **Listing 5.5: `get_cursor_at()`** ```c void get_cursor_at( Buffer buf, Window win, int index, int lineno, Cursor *curs ) { int total_lines_before = 0; int rows_in_current_textline = 0; int i; /* The first line is the one at the top of the window, whose index is win.line_at_top; initially 0. */ for ( i = Win.line_at_top; i < lineno; i++ ) { if ( buf.line_len[i] < win.cols ) total_lines_before++; else total_lines_before += (int) ceil((double)buf.line_len[i]/win.cols); } rows_in_current_textline = index/win.cols; curs->r = total_lines_before + rows_in_current_textline; curs->c = index - rows_in_current_textline * win.cols; } ``` #### 6. Product Analysis: - **Products**: This analysis does not apply as there are no products depicted in the visual content. #### 8. Color Analysis: - **Color Composition**: - The document is primarily in grayscale, with black text on a white background. This standard academic or professional document color scheme ensures readability and focus on the text content. #### 9. Perspective and Composition: - **Perspective**: - The image presents a typical scanned or snapshot view of a text document presented directly from an overhead perspective. - **Composition**: - The text is arranged in paragraphs with appropriate headings, followed by code snippets. Listings and explanations are clearly aligned. #### 11. Metadata Analysis: - **Metadata**: - The page comes from “UNIX Lecture Notes” by Prof. Stewart Weiss. - Chapter and focus: "Chapter 5 Interactive Programs and Signals". - Large emphasis is placed on properly licensing the document under Creative Commons. #### Additional Aspects: - **Prozessbeschreibungen (Process Descriptions)**: - The text thoroughly describes processes for handling buffer indexing and cursor positioning within a text editor. This exhaustive analysis aimed to provide comprehensive detail based on each specified aspect, focusing primarily on textual and contextual analysis due to the nature of the visual content. #################### File: UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf Page: 13 Context: # UNIX Lecture Notes ### Prof. Stewart Weiss #### Chapter 5: Interactive Programs and Signals The current position in the buffer may be different than the current one. In the latter case, it is moved to the end of the line. The complication is that this position may be out of view, in which case the text must be scrolled upwards to put it into view. Therefore, the function to do this must move the cursor logically and then check whether this logical position is out of view. This requires: 1. advancing `cur_line` to the line below if not on the last line; 2. adjusting `index_in_cur_line` if the line is too short; 3. getting the index of the last line in the window; 4. getting the actual cursor position; 5. if the cursor is below the last line, calculating by how much scrolling accordingly; and 6. moving the cursor to the new position on the screen and drawing it. The `moveDown()` function in the listing below encapsulates this logic. ### Listing 5.6: moveDown() ```c void moveDown(Buffer *buf, Window *win, Cursor *curs) { int lastline; if (buf->cur_line < buf->num_lines - 1) { buf->cur_line++; if (buf->index_in_cur_line >= buf->line_len[buf->cur_line]) { buf->index_in_cur_line = buf->line_len[buf->cur_line] - 1; } } get_lastline_in_win(&buf, &win, &lastline); if (buf->cur_line > lastline) { win->line_at_top += buf->cur_line - lastline; get_cursor_at(&buf, &win, buf->index_in_cur_line, buf->cur_line, curs); buf->cur_line = curs; scroll_buffer(&buf, &win); } else { get_cursor_at(&buf, &win, buf->index_in_cur_line, buf->cur_line, curs); moveto(curs->r, curs->c); } } ``` ## 5.2.2.3 Terminal Interaction Terminal interaction includes modifying or querying the terminal state, obtaining the window size and the erase character, and writing various ANSI escape sequences to do things such as clearing parts of the screen or moving the cursor. This work is copyrighted by Stewart Weiss and licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. Image Analysis: ### Image Analysis #### 1. Localization and Attribution - **Image 1**: Entire page content. #### 2. Object Detection and Classification - **Image 1**: - **Text blocks**: Various paragraphs, code listing, and section headers. - **Code Block**: A block of C code that is a function definition. - **Header and Footers**: Including the section title and footer with publication information. #### 3. Scene and Activity Analysis - **Image 1**: - The page is from a lecture note/book on UNIX, specifically discussing interactive programs and signals. - The main actors are textual descriptions and a code snippet explaining a function in C programming language. - Activities include the explanation and demonstration of the `moveDown()` function. #### 4. Text Analysis - **Image 1**: - **Title**: "UNIX Lecture Notes" and "Chapter 5 Interactive Programs and Signals". - **Section Heading**: "5.2 This requires" and "5.2.2.3 Terminal Interaction". - **Code Listing Title**: "Listing 5.6: moveDown()". - **Paragraph Content**: Explains the concept of moving a cursor in the context of a text buffer, including detailed steps required to correctly execute the operation. - **Code Snippet**: ```c void moveDown( Buffer *buf, Window *win, Cursor *curs ) { int lastline; if ( buf->cur_line < buf->num_lines-1 ) { buf->cur_line++; if ( buf->index_in_cur_line >= buf->line_len[buf->cur_line] ) { buf->index_in_cur_line = buf->line_len[buf->cur_line]-1; } } get_lastline_in_win ( *buf, *win, &lastline ); if ( buf->cur_line > lastline ) { win->line_at_top += buf->cur_line - lastline; get_cursor_at(*buf, *win, buf->index_in_cur_line, buf->cur_line, curs ); scroll_buffer(*buf, *win ); } else get_cursor_at(*buf, *win, buf->index_in_cur_line, buf->cur_line, curs ); moveto(curs->r, curs->c ); } ``` - **Footnote**: Indicates copyright information attributed to Stewart Weiss under Creative Commons Attribution-ShareAlike 4.0 International License. #### 6. Product Analysis - **Image 1**: - **Product**: The function `moveDown()` in C programming. - **Features**: - Moves the cursor down within a text buffer. - Handles cases where the new cursor position might be out of view. - Adjusts `index_in_cur_line` and `cur_line`. - Scrolls the buffer when necessary. - Calculates new cursor positions and draws them. #### 9. Perspective and Composition - **Image 1**: - **Perspective**: Straightforward, documentation view. - **Composition**: - The composition follows a standard technical document format. - The top section includes the book’s title and chapter name. - The middle and bottom sections contain explanatory text and a code snippet. #### 10. Contextual Significance - **Image 1**: - This page contributes to the overall message of understanding movement functions within UNIX terminal programming. - Provides detailed examples for practical understanding of how cursor movement is handled in a buffered window system. #### 13. Ablaufprozesse (Process Flows) - **Image 1**: - The process flow includes: 1. Advancing `cur_line`. 2. Adjusting `index_in_cur_line`. 3. Getting the last line index. 4. Getting actual cursor position. 5. Calculating scrolling if necessary. 6. Moving cursor and drawing it. #### 14. Prozessbeschreibungen (Process Descriptions) - **Image 1**: - Describes the steps necessary to move a cursor down within a text buffer in a terminal environment, ensuring the new cursor position is visible to the user. ########## """QUERY: Please summarize the whole context. It is important that you include a summary for each file. All files should be included, so please make sure to go through the entire context""" Consider the chat history for relevant information. Use all information included. Use as much tokens as needed. Important: If you find information separated by a | in the context, it is a table formatted in Markdown. Final Files Sources: Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20%20UsingOberon.pdf - Page 1, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20%20UsingOberon.pdf - Page 2, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20%20UsingOberon.pdf - Page 3, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20%20UsingOberon.pdf - Page 4, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 1, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 2, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 3, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 4, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 5, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 6, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 7, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 8, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 9, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 10, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 11, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 12, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 13, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 14, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 15, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 16, BIOS%20Disassembly%20Ninjutsu%20Uncovered%201st%20Edition%20-%20Darmawan%20Salihun%20%28PDF%29%20POST_Memory_Manager_Specification.pdf - Page 17, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 1, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 2, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 3, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 4, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 5, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 6, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 7, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 8, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 9, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 10, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 11, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 12, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 13, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 14, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 15, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 16, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 17, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 18, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 19, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 20, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 21, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 22, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 23, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 24, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 25, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 26, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 27, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 28, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 29, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 30, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 31, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 32, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 33, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 34, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 35, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 36, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 37, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 38, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 39, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 40, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 41, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 42, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 43, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 44, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 45, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 46, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 47, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 48, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 49, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 50, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 51, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 52, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 53, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 54, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 55, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 56, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 57, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 58, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 59, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 60, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 61, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 62, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 63, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 64, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 65, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 66, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 67, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 68, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 69, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 70, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 71, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 72, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 73, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 74, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 75, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 76, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 77, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 78, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 79, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 80, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 81, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 82, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 83, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 84, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 85, Project%20Oberon%20The%20Design%20of%20an%20Operating%20System%2C%20a%20Compiler%2C%20and%20a%20Computer%20-%20Niklaus%20Wirth%2C%20J%C3%BCrg%20Gutknecht%20%28PDF%29%20PO.Applications.pdf - Page 86, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 1, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 2, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 3, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 4, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 5, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 6, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 7, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 8, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 9, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 10, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 11, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 12, UNIX%20Application%20and%20System%20Programming%2C%20lecture%20notes%20-%20Stewart%20Weiss%20%28PDF%29%20chapter_05.pdf - Page 13 ================================================== **Elapsed Time: 68.84 seconds** ================================================== FINAL ANSWER Answer: ================================================== **Elapsed Time: 0.00 seconds** ==================================================