TOPS-20 Monitor Calls User's Guide

Chapter 4 Using the software interrupt system

4.1 Overview

Program execution usually occurs in a sequential manner, where instructions are executed one after another. But sometimes a program must be able to receive asynchronous signals from terminals, the monitor, or other programs, or as a result of its own execution. By using the software interrupt system, the user can specify conditions that will cause his program to deviate from its sequential method of execution.

An interrupt is defined as a break in the normal flow of control during a program's execution. The break, or interrupt, is caused by the occurrence of a prespecified condition. By specifying the conditions that can cause an interrupt, the program has the capability of dynamically responding to external events and error conditions and of generating requests for services. Because the program can respond to special conditions as they occur, it does not have to explicitly and repeatedly test for them. In addition, the program's execution is faster because the program does not have to include a special test after the possible occurrence of the condition.

When an interrupt occurs, the system transfers control from the main program sequence to a previously-specified routine that will process the interrupt. After the routine has completed its processing of the interrupt, the system can transfer control back to the program at the point it was interrupted, and execution can continue. See Figure 4-1.

     -----------------
     | User Program  |
     |     is        |
     | Executing     |
     -----------------
             |
             V
     -----------------
     | Interrupt     |
     | Condition     |
     | Occurs        |
     -----------------
             |
             V
             ^                          ^
            / \                        / \
           /   \                      /   \
          / Has \                    /Is An\
         /Program\                  /Inter- \          
        / Enabled \                /rupt of  \          -----------------
       /for Condi- \  Yes         /Higher Pri-\ No      | Execute       |
      <tion on this >----------->< ority Being >------->| User's Inter- |
       \ Channel   /      ^       \ Processed /         | rupt Routine  |
        \   ?     /       |        \    ?    /          --------|--------
         \       /        |         \       /                   |
          \     /         |          \     /                    |
           \   /          |           \   /                     |
            \ /           |            \ /                      |
             V            |             V                       |
             | No         |             | Yes                   |
             |            |             V                       |
             |            |     ----------------------          |
             V            |     | Wait Until         |          |
     -----------------    |     | Higher Priority    |          |
     | Perform System|    |     | Interrupt Finishes |          |
     | Default Action|    |     --------|-------------          |        
     | (e.g., stops  |    |             |                       |
     | job, print    |    \-------------/                       |
     | message)      |                                          |
     --------|--------                                          |
             | <------------------------------------------------/
             V
     -----------------
     | User Program  |
     | Continues if  |
     | Job Has Not   |
     | Not Been      |
     | Terminated    |
     -----------------
Figure 4-1: Basic Operational Sequence of the Software Interrupt System

4.2 Interrupt conditions

Conditions that cause the program to be interrupted when the interrupt system is enabled are:

  1. Conditions generated when specific terminal keys are typed. There are 36 possible codes; each one specifies the particular terminal character or condition on which an interrupt is to be initiated. Refer to Table 4-2 for the possible codes.
  2. Invalid instructions (for example, I/O instructions given in user mode) or privileged monitor calls issued by a non privileged user.
  3. Memory conditions, such as illegal memory references.
  4. Arithmetic processor conditions, such as arithmetic overflow or underflow.
  5. Certain file or device conditions, such as end of file.
  6. Program-generated software interrupts.
  7. Termination of an inferior process.
  8. System resource unavailability.
  9. Interprocess communication (IPCF) and Enqueue/Dequeue interrupts.

4.3 Software interrupt channels and priorities

Each condition is associated with one of 36 software interrupt channels. Most conditions are permanently assigned to specific channels; however, the user's program can associate some conditions (for example, conditions generated by specific terminal keys) to any one of the assignable channels. (Refer to Table 4-1 for the channel assignments.) When the condition associated with a channel occurs, and that channel has been activated, an interrupt is generated. Control can then be transferred to the routine responsible for processing interrupts on that channel.

The user program assigns each channel to one of three priority levels. Priority levels allow the occurrence of some conditions to suspend the processing of other conditions. The levels are referred to as level 1, 2, or 3 with level 1 having the highest priority. Level 0 is not a legal priority level.[1]

Table 4-1: Software Interrupt Channel Assignments
ChannelSymbolMeaning
0-5 Assignable by user program
6 .ICAOV Arithmetic overflow
7 .ICFOV Arithmetic floating point overflow
8 Reserved for Digital
9 .ICPOV Pushdown list (PDL) overflow*
10 .ICEOF End of file condition
11 .ICDAE Data error file condition*
12 .ICQTA Disk quota exceeded
13-14 Reserved for Digital
15 .ICILI Illegal instruction*
16 .ICIRD Illegal memory read*
17 .ICIWR Illegal memory write*
18 Reserved for Digital
19 .ICIFT Inferior process termination
20 .ICMSE System resources exhausted*
21 Reserved for Digital
22 .ICNXP Nonexistent page reference
23-35 Assignable by user program

* These channels (called panic channels) cannot be completely deactivated. An interrupt generated on one of these channels terminates the process if the channel is not activated.

The software interrupt system processes interrupts on activated channels only, and each channel can be activated and deactivated independently of other channels. When activated, the channel can generate an interrupt for its associated priority level. An interrupt for any priority level is initiated only if there are no interrupts in progress for the same or higher priority levels. If there are, the system remembers the interrupt request and initiates it after all equal or higher priority level interrupts finish. This means that a higher priority level request can suspend a routine processing a lower level interrupt. Thus, the user must be concerned with several items when he assigns his priority levels. He must consider 1) when one interrupt request can suspend the processing of another and 2) when the processing of a second interrupt cannot be deferred until the completion of the first.

4.4 Software interrupt tables

To process interrupts, the user includes, as part of his program, special service routines for the channels he will be using. He must then specify the addresses of these routines to the system by setting up a channel table. In addition, the user must also include a priority level table as part of his program. Finally, he must declare the addresses of these tables to the system.

4.4.1 Specifying the Software Interrupt Tables

Before using the software interrupt system, the user's program must set up the contents of the channel table and the priority level table. The program must then specify their addresses with either the SIR% or XSIR% monitor calls.

These calls are similar, but their differences are important. The SIR% call can be used in single-section programs, but the XSIR% call must be used in programs that use more than one section of memory. The SIR% call works in non-zero sections only if the tables are in the same section as the code that makes the call. The code that causes the interrupt must also be in that section, as must the code that processes the interrupt. Because of the limitations of the SIR% call, you should use the XSIR% call.

The SIR% monitor call accepts two words of arguments: the identifier for the program (or process) in AC1, and the table addresses in AC2. Refer to Section 5.3 for the description of process identifiers.

The following example shows the use of the SIR% call.

        MOVEI 1,.FHSLF           ;identifier of current process
        MOVE 2,[LEVTAB,,CHNTAB]  ;addresses of the tables
        SIR%

The XSIR% call accepts the following arguments: in AC1, the identifier of the process for which the interrupt channel tables are to be set; in AC2, the address of the argument block.

The argument block is a three-word block that has the following format:

           !======================================================!
           !   Length of the argument block, including this word  !
           !------------------------------------------------------!
           !          Address of the interrupt level table        !
           !------------------------------------------------------!
           !             Address of the channel table             !
           !======================================================!

Control always returns to the user's program at the instruction following the SIR% and XSIR% calls. If the call is successful, the table addresses are stored in the monitor. If the call is not successful, an illegal instruction trap is generated.

Any changes made to the contents of the tables after the XSIR% or SIR% calls have been executed will be in effect at the time of the next interrupt.

4.4.2 Channel Table

The channel table, CHNTAB,[2] contains a one-word entry for each channel; thus, the table has 36 entries. Each entry corresponds to a particular channel, and each channel is associated at any given time with only one interrupt condition. (Refer to Table 4-1 for the interrupt conditions associated with each channel.)

The CHNTAB table is indexed by the channel number (0 through 35). The general format, for use with the XSIR% and XRIR% monitor calls, can be used in any section of memory. The left half of each entry contains the priority level (1, 2, or 3) in bits 0-5 (SI%LEV) to which the channel is assigned. Bits 6-35 (SI%ADR) of each entry contain the starting address of the routine to process interrupts generated on that channel. If a particular channel is not used, the corresponding entry in the channel table should be zero.

In the older format, for use with the SIR% and RIR% calls by any single-section program, the left half of each word contains the priority level (1, 2, or 3) for that channel. The right half contains the address of the interrupt routine that will handle interrupts on that channel.

The following example is for use with the XSIR% monitor call.

   CHNTAB: FLD(2,SI%LEV)+FLD(CHN0SV,SI%ADR)        ;channel 0
           FLD(2,SI%LEV)+FLD(CHN1SV,SI%ADR)        ;channel 1
           FLD(2,SI%LEV)+FLD(CHN2SV,SI%ADR)        ;channel 2
           FLD(2,SI%LEV)+FLD(CHN3SV,SI%ADR)        ;channel 3
           0                                       ;channel 4
           0                                       ;channel 5
           FLD(1,SI%LEV)+FLD(APRSRV,SI%ADR)        ;channel 6
           0                                       ;channel 7
           0                                       ;channel 8
           FLD(1,SI%LEV)+FLD(STKSRV,SI%ADR)        ;channel 9
           0                                       ;channel 10
            .                                        .
            .                                        .
            .                                        .
           0                                       ;channel 35

In this example, channels 0 through 3 are assigned to priority level 2, with the interrupt routine at CHN0SV servicing channel 0, the routine at CHN1SV servicing channel 1, the routine at CHN2SV servicing channel 2, and the routine at CHN3SV servicing channel 3. Channels 6 and 9 are assigned to priority level 1, with the routine at APRSRV servicing channel 6 and the routine at STKSRV servicing channel 9. All remaining channels are not assigned.

4.4.3 Priority Level Table

The priority level table, LEVTAB,[3] The priority level table, LEVTAB, is a three-word table, containing a one-word entry for each of the three priority levels. In the general form, each word contains the 30-bit address of the first word of the two-word block in the process address space. The block addressed by word n of LEVTAB is used to store the global PC flags and address when an interrupt of level n+1 occurs.

The PC flags are stored in the first word of the PC block, and the PC address is stored in the second. This form of the table must be used with the XSIR% and XRIR% monitor calls, and can be used in any section.

The older form of the interrupt level table can be used in any single-section program, and must be used with the SIR% and RIR% calls. This table also contains three words, indexed by the priority level minus 1. Each word contains zero in the left half, and the 18-bit address of the word in which to store the one-word section-relative PC in the right half. This address is assumed to be in the same program section that contained the SIR% monitor call. (For more information see Chapter 8.) The system must save the value of the program counter so that it can return control at the appropriate point in the program once the interrupt routine has completed processing an interrupt. If a particular priority level is not used, its corresponding entry in the level table should be zero.

The following is a sample of a level table.

        LEVTAB:   0,,PCLEV1       ;Addresses to save PC for interrupts
                  0,,PCLEV2       ;occurring on priority levels 1 and 2.
                  0,,0            ;No priority level 3 interrupts are
                                  ;planned

4.5 Enabling the software interrupt system

Once the interrupt tables have been set up and their addresses defined with the XSIR% monitor call, the user's program must enable the interrupt system. When the interrupt system is enabled, interrupts that occur on activated channels are processed by the user's interrupt routines. When the interrupt system is disabled, the monitor processes interrupts as if the channels for these interrupts were not activated.

The EIR% monitor call, used to enable the system, accepts one argument: the identifier for the process in AC1.

        MOVEI 1,.FHSLF           ;identifier of current process
        EIR%

Control always returns to the instruction following the EIR call.

4.6 Activating interrupt channels

Once the software interrupt system is enabled, the channels on which interrupts can occur must be activated (refer to Table 4-1 for the channel assignments). The channels to be activated have a nonzero entry in the appropriate word in the channel table.

The AIC% monitor call activates one or more of the 36 interrupt channels. This call accepts two words of arguments - the identifier for the process in AC1, and the channels to be activated in AC2.

The channels are indicated by setting bits in AC2. Setting bit n indicates that channel n is to be activated. The AIC% call activates only those channels for which bits are set.

        MOVEI 1,.FHSLF                     ;identifier of current process
        MOVE 2,[1B<.ICAOV>+1B<.ICPOV>]     ;activate channels 6 and 9
        AIC%

Control always returns to the instruction following the AIC% call.

Some channels, called panic channels, cannot be deactivated by disabling the channel or the entire interrupt system. (Refer to Table 4-1 for these channels.) This is because the occurrence of the conditions associated with these channels cannot be completely ignored by the monitor.

If one of these conditions occurs, an interrupt is generated whether the channel is activated or not. If the channel is not activated, the process is terminated, and usually a message is output before control returns to the monitor. If the channel is activated, control is given to the user's interrupt routine for that channel.

4.7 Generating an interrupt

A process generates an interrupt by producing a condition for which an interrupt channel is enabled, such as arithmetic overflow, or by using the IIC% monitor call. This call can generate an interrupt on any of the 36 interrupt channels of the process the calling process specifies. See Section 5.10 for a description of the IIC% call.

4.8 Processing an interrupt

When a software interrupt occurs on a given priority level, the monitor stores the current program counter (PC) word in the address indicated in the priority level table (refer to Section 4.4.3). The monitor then transfers control to the interrupt routine associated with the channel on which the interrupt occurred. The address of this routine is specified in the channel table (refer to Section 4.4.2).

Since the user's program cannot determine when an interrupt will occur, the interrupt routine must preserve the state of the program so the program can be resumed properly. First, the routine stores the contents of any user accumulators for use while processing the interrupt. After the accumulators are saved, the interrupt routine processes the interrupt.

Occasionally, an interrupt routine may need to alter locations in the main section of the program. For example, a routine may change the stored PC word to resume execution at a location different from where the interrupt occurred. Or it may alter a value that caused the interrupt. It is important that care be used when writing routines that alter data because any changes will remain when control is returned to the main program. For example, if data is inadvertently stored in the PC word, return to the main section of the program would be incorrect when the system attempted to use the word as the value of the program counter.

If a higher-priority interrupt occurs during the execution of an interrupt routine, the execution of the lower-priority routine is suspended. The value of its program counter is stored at the location indicated in the priority level table for the new interrupt. When the routine for this new interrupt is completed, the suspended routine resumes.

If an interrupt of the same or lower priority occurs during the execution of a routine, the monitor holds the interrupt until all higher or equal level interrupts have been processed.

The system considers the user's program unable to process an interrupt on an activated channel if any of the following is true:

  1. The priority level associated with the channel is 0.
  2. The program has not defined its interrupt tables by executing an XSIR% or SIR% monitor call.
  3. The process has not enabled the interrupt system by executing an EIR% monitor call, and the channel on which the interrupt occurs is a panic channel.

In any of these cases, an interrupt on a panic channel terminates the user's program. All other interrupts are ignored.

4.8.1 Dismissing an Interrupt

Once the processing of an interrupt is complete, the interrupt routine should restore the user accumulators to their initial values. Then it should return control to the interrupted code by using the DEBRK% monitor call. This call restores the PC word and resumes the program. The call has no arguments, and must be the last statement in the interrupt routine.

If the interrupt-processing routine has not changed the PC of the user's program, the DEBRK% call restores the program to the same state the program was in just before the interrupt occurred. If the program was interrupted while waiting for I/O to complete, for example, the program will again be waiting for I/O to complete when it resumes execution after the DEBRK% call.

If the PC word was changed, the program resumes execution at the new PC location. The state of the program is unchanged.

4.9 Terminal interrupts

The user's program can associate channels 0 through 5 and channels 24 through 35 with occurrences of various conditions, such as the occurrence of a particular character typed at the terminal or the receipt of an IPCF message. This section discusses terminal interrupts; refer to Chapters 6 and 7 for other types of assignable interrupts.

There are 36 codes used to specify terminal characters or conditions on which interrupts can be initiated. These codes, along with their associated conditions, are shown in Table 4-2.

Table 4-2: Terminal Codes and Conditions
CodeSymbolCharacter or Condition
0 .TICBK CTRL/@ or break
1 .TICCA CTRL/A
2 .TICCB CTRL/B
3 .TICCC CTRL/C
4 .TICCD CTRL/D
5 .TICCE CTRL/E
6 .TICCF CTRL/F
7 .TICCG CTRL/G
8 .TICCH CTRL/H
9 .TICCI CTRL/I
10 .TICCJ CTRL/J
11 .TICCK CTRL/K
12 .TICCL CTRL/L
13 .TICCM CTRL/M
14 .TICCN CTRL/N
15 .TICCO CTRL/O
16 .TICCP CTRL/P
17 .TICCQ CTRL/Q
18 .TICCR CTRL/R
19 .TICCS CTRL/S
20 .TICCT CTRL/T
21 .TICCU CTRL/U
22 .TICCV CTRL/V
23 .TICCW CTRL/W
24 .TICCX CTRL/X
25 .TICCY CTRL/Y
26 .TICCZ CTRL/Z
27 .TICES ESC key
28 .TICRB Delete (or rubout) key
29 .TICSP Space
30 .TICRF Dataset carrier off
31 .TICTI Typein
32 .TICTO Typeout
33 .TITCE Two-character escape sequence
34-35 Reserved

To cause terminal interrupts to be generated, the user's program must assign the desired terminal code to one of the assignable channels.

The ATI% monitor call is used to assign this code. This call accepts one word of arguments: the terminal code in the left half of AC1 and the channel number in the right half.

        MOVE 1,[.TICCE,,INTCH1]  ;assign CTRL/E to channel INTCH1
        ATI%

Control always returns to the instruction following the ATI% call. If the current job is not attached to a terminal (there is no terminal controlling the job), the terminal code assignments are remembered; they will be in effect when a terminal is attached.

The monitor handles the receipt of a terminal interrupt character in either immediate mode or deferred mode. In immediate mode, the terminal character causes the system to initiate an interrupt as soon as the user types the character (that is, as soon as the system receives it). In deferred mode, the terminal character is placed in either immediate mode or deferred mode. In immediate mode, the terminal character causes the system to initiate an interrupt as soon as the user types the character (as soon as the system receives it). In deferred mode, the terminal character is placed in the input stream in sequence with other characters of the input, unless two of the same character are typed in succession. In this case, an interrupt occurs at the time the second one is typed. If only one character enabled in deferred mode is typed, the system initiates an interrupt only when the program attempts to read the character. Deferred mode allows interrupt actions to occur in sequence with other actions specified in the input (for example, when characters are typed ahead of the time that the program actually requests them). In either mode, the character is not passed to the program as data. The system assumes that interrupts are to be handled immediately unless a program has issued the STIW% (Set Terminal Interrupt Word) monitor call. (Refer to TOPS-20 Monitor Calls Reference Manual for a description of this call.)

4.10 Additional software interrupt monitor calls

Additional monitor calls are available that allow the user's program to check and to clear various parts of the software interrupt system. Also, there is a call useful for interprocess communication (refer to the IIC% call in Section 5.10).

4.10.1 Testing for Enablement

The SKPIR% monitor call tests the software interrupt system to see if it is enabled. The call accepts in AC1 the identifier of the process. After execution of the call, control returns to the next instruction if the system is off, and to the second instruction if the system is on.

        MOVEI 1,.FHSLF           ;identifier of current process
        SKPIR%                   ;test interrupt system
         return                  ;system is off
        return                   ;system is on

4.10.2 Obtaining Interrupt Table Addresses

The RIR% and XRIR% monitor calls obtain the channel and priority level table addresses for a process. These calls are useful when several routines in one process want to share the interrupt tables.

4.10.2.1 The RIR% Monitor Call

The RIR% monitor call can be used in any section of memory, but is only useful for obtaining table addresses if those tables are in the same section of memory as the code that makes the call. Furthermore, it can only obtain table addresses that have been set by the SIR call.

The call accepts the identifier of the process in AC1. It returns the table addresses in AC2. The left half of AC2 contains the section-relative address of the priority level table, and the right half contains the section-relative address of the channel table. If the process has not set the table addresses with the SIR% monitor call, AC2 contains zero.

Control always returns to the instruction following the RIR% call.

The following example shows the use of the RIR% call.

        MOVEI 1,.FHSLF           ;identifier of current process
        RIR%                     ;return the table addresses
4.10.2.2 The XRIR% Monitor Call

This call obtains the addresses of the interrupt tables defined for a process. The tables can be in any section of memory. The code that makes the call can also be in any section. This call can only obtain addresses that have been set by the XSIR% call.

The call accepts the identifier of the process in AC1, and the address of the argument block in AC2. The argument block is three words long, word zero must contain the number 3. The call returns the addresses into words one and two. The block has the following format:

           !=======================================================!
           !   Length of the argument block, including this word   !
           !-------------------------------------------------------!
           !          Address of the interrupt level table         !
           !-------------------------------------------------------!
           !             Address of the channel table              !
           !=======================================================!

Control always returns to the instruction following the XRIR% call. If the process has not set the table addresses with the XSIR% monitor call, words one and two of the argument block contain zero.

4.10.3 Disabling the Interrupt System

The DIR% monitor call disables the software interrupt system for the process. It accepts the identifier of the process in AC1.

        MOVEI 1,.FHSLF           ;identifier of current process
        DIR%                     ;disable system

Control always returns to the instruction following the DIR% call.

If interrupts occur while the interrupt system is disabled, they are remembered until the system is reenabled. At that time, the interrupts take effect unless an intervening CIS% monitor call (refer to Section 4.10.6) has been issued.

Software interrupts assigned to panic channels are not completely disabled by the DIR% call. These interrupts terminate the process, and the superior process is notified if it has enabled channel .ICIFT. In addition, if the terminal code for CTRL/C (.TICCC) is assigned to a channel, it causes an interrupt that cannot be disabled by the DIR% call. However, the CTRL/C interrupt can be disabled by deactivating the channel assigned to the CTRL/C terminal code.

4.10.4 Deactivating a Channel

The DIC% monitor call is used to deactivate interrupt channels. The call accepts two words of arguments: the process identifier in AC1, and the channels to be deactivated in AC2. Setting bit n in AC2 indicates that channel n is to be deactivated.

        MOVEI 1,.FHSLF                     ;identifier of current process
        MOVE 2,[1B<.ICAOV>+1B<.ICPOV>]     ;deactivate channels 6 and 9
        DIC%

Control always returns to the instruction following the DIC% call.

When a channel is deactivated, interrupt requests for that channel are ignored except for interrupts generated on panic channels (refer to Section 4.6).

4.10.5 Deassigning Terminal Codes

The DTI% monitor call deassigns a terminal code. This call accepts one argument word: the terminal code in AC1.

        MOVEI 1,.TICCE           ;deassign CTRL/E
        DTI%

Control always returns to the instruction following the DTI% call. This monitor call is ignored if the specified terminal code has not been defined by the current job.

4.10.6 Clearing the Interrupt System

The CIS% monitor call clears the interrupt system for the current process. This call clears interrupts in progress and all waiting interrupts. This call requires no arguments, and control always returns to the instruction following the CIS call. The RESET% monitor call (refer to Section 2.6.1) performs these same actions as part of its initializing procedures.

4.11 Summary

To use the software interrupt system, the user's program must:

  1. Supply routines that will process the interrupts.
  2. Set up a channel table containing the addresses of the routines (refer to Section 4.4.2) and a priority level table containing the addresses for storing the program counter (PC) values (refer to Section 4.4.3).
  3. Specify the addresses of the tables with the XSIR% monitor call (refer to Section 4.4.3).
  4. Enable the software interrupt system with the EIR% monitor call (refer to Section 4.5).
  5. Activate the desired channels with the AIC% monitor call (refer to Section 4.6).

4.12 Software interrupt example

This program copies one file to another. It accepts the input and output filenames from the user. The end of file is detected by a software interrupt, and CTRL/E is enabled as an escape character.

           TITLE SOFTWARE INTERRUPT EXAMPLE
           SEARCH MONSYM
           SEARCH MACSYM
           .REQUIRE SYS:MACREL

           STDAC.                  ;DEFINE STANDARD ACs
           INTCH1=1

   START:  RESET%                  ;RELEASE FILES, ETC.
           XHLLI T1,EOFINT         ;GET CURRENT PROCESS SECTION NUMBER
           HLLZS T1                ;ISOLATE SECTION NUMBER ONLY
           IORM T1,CHNTAB+INTCH1   ; AND ADD IT TO SERVICE ROUTINE 
           IORM T1,CHNTAB+.ICEOF   ;ADDRESSES FOR OUR ROUTINES
           IORM T1,LEVTAB+1        ; AND LEVTAB
           MOVEI T1,.FHSLF         ;CURRENT PROCESS
           MOVEI T2,3              ;NUMBER OF WORDS IN ARG BLOCK
           MOVEM T2,ARGBLK         ;PUT NUMBER IN WORD ZERO
           XMOVEI T2,LEVTAB        ;GLOBAL ADDRESS OF LEVEL TABLE
           MOVEM T2,ARGBLK+1       ;MOVE IT TO ARGBLK WORD ONE
           XMOVEI T2,CHNTAB        ;GLOBAL ADDRESS OF CHANNEL TABLE
           MOVEM T2, ARGBLK+2      ;MOVE IT TO ARGBLK WORD TWO
           XMOVEI T2,ARGBLK        ;GLOBAL ADDRESS OF ARGUMENT BLOCK
           XSIR%
           EIR%                    ;ENABLE SYSTEM
           MOVE T2,[1B<INTCH1>+1B<.ICEOF>] ;ACTIVATE CHANNELS
           AIC%
           MOVE T1,[.TICCE,,INTCH1] ;ASSIGN CTRL/E TO CHANNEL 1
           ATI%
   GETIF:  TMSG <INPUT FILE: >
           MOVX T1,GJ%OLD+GJ%MSG+GJ%CFM+GJ%FNS+GJ%SHT
           MOVE T2,[.PRIIN,,.PRIOU]
           GTJFN%                  ;GET FILENAME FROM USER
            ERJMP ERROR1
           MOVEM T1,INJFN
   GETOF:  TMSG <OUTPUT FILE: >
           MOVX T1,GJ%FOU+GJ%MSG+GJ%CFM+GJ%FNS+GJ%SHT
           MOVE T2,[.PRIIN,,.PRIOU]
           GTJFN%                  ;GET FILENAME FROM USER
            ERJMP ERROR2
           MOVEM T1,OUTJFN
   OPNIF:  MOVE T1,INJFN
           MOVX T2,FLD(7,OF%BSZ)+OF%RD
           OPENF%                  ;OPEN INPUT FILE
            ERJMP ERROR3
   OPNOF:  MOVE T1,OUTJFN
           MOVX T2,FLD(7,OF%BSZ)+OF%WR
           OPENF%                  ;OPEN OUTPUT FILE
            ERJMP ERROR3
   CPYBYT: MOVE T1,INJFN
           BIN%                    ;READ INPUT BYTE
           MOVE T1,OUTJFN
           BOUT%                   ;WRITE OUTPUT BYTE
           JRST CPYBYT             ;LOOP UNTIL EOF
   DONE:   MOVE T1,INJFN
           CLOSF%                  ;CLOSE INPUT FILE
            JFCL
           MOVE T1,OUTJFN
           CLOSF%                  ;CLOSE OUTPUT FILE
            JFCL
           HALTF%
   ;ROUTINE TO HANDLE ^E - ABORTS OPERATION

   CTRLE:  MOVEI T1,.PRIOU
           CFOBF%                  ;CLEAR OUTPUT BUFFER
           TMSG <ABORTED.>         ;INFORM USER
           CIS%                    ;CLEAR SYSTEM
           JRST START

   ;ROUTINE TO HANDLE EOF - COMPLETES OPERATION NORMALLY

   EOFINT: MOVEM T1,INTAC1         ;SAVE ACs
           XMOVEI T1,DONE          ;CHANGE PC
           MOVEM T1,PC2+1          ;TO DONE
           MOVE T1,INTAC1          ;RESTORE ACs
           DEBRK%                  ;DISMISS INTERRUPT

   ;LEVEL TABLE
   LEVTAB: 0
           PC2
           0
   PC2:    BLOCK 2

   ;CHANNEL TABLE
   CHNTAB: 0
           FLD(2,SI%LEV)!FLD(CTRLE,SI%ADR)
           REPEAT ^D8,<0>
           FLD(2,SI%LEV)!FLD(EOFINT,SI%ADR)
           REPEAT ^D25,<0>
   ARGBLK: BLOCK 3
   INJFN:  BLOCK 1
   OUTJFN: BLOCK 1
   INTAC1: BLOCK 1
   ERROR1: TMSG <
   ?INVALID FILE SPECIFICATION>
           HALTF%
   ERROR2: TMSG <
   ?INVALID FILE SPECIFICATION>
           HALTF%
   ERROR3: TMSG <
   ?CANNOT OPEN FILE>
           HALTF%
           LIT
           END START

[1] If an interrupt is generated in a process where the priority level is 0, the system considers that the process is not prepared to handle the interrupt. The process is then suspended or terminated according to the setting of bit 17 (SC%FRZ) in its capability word.

[2] The user can call his priority channel table any name he desires; however, it is good practice to call it CHNTAB.

[3] The user can call his priority level table any name he desires; however, it is good practice to call it LEVTAB.