\def\acp{ACP}
\def\irp{IRP}
\def\vms{VMS}
\def\pas{Pascal}
\def\mac{MACRO}
\def\cmkd{change-mode-to-kernel dispatcher}

\begin{slide}{} \parbox{\textwidth}{
\begin{centering}
{\large ``But That's Impossible in \pas''}\\ {\it or}\\
{\large Systems Programming in a High-Level Language}
\vskip 0.5 in    
E. W. (Wayne) Sewell\\
E-Systems, Garland Division\\
Dallas, Texas\\
\end{centering}
}
\end{slide} 
\begin{slide}{} \parbox{\textwidth}{
Topics :
\begin{itemize}
\item \fbox {Special Requirements of Systems Programming}
\item The System Control Blocks
\item A Trivial Example Program 
\item A \pas\ \acp
\item Conclusions
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Special Requirements of Systems Programming (all require kernel mode) :
\begin{itemize}
\item Accessing the nonpaged pool
\item Locking the I/O database
\item MUTEXes
\item Raised IPL
\item Access to VMS routines in system space
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Problems encountered when trying to use kernel mode with high-level languages :
\begin{itemize}
\item There are severe restraints placed on code running in kernel
\item Access violations are catastrophic for the entire VAX, not just the 
process---to avoid them the {\tt PROBE} instruction is used
\item Most calls to system routines are via the {\tt JSB} instruction with
parameters in specific registers
\item Compiler output cannot be controlled 
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
So why not do it in \mac\ and be done with it?
\begin{itemize}
\item It is tedious to write entire programs in \mac
\item Some classes of system programs only need kernel mode at
distinct points --- the rest of the processing can be done in user mode
\item During development, trivial logic errors can crash the entire system
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
So what's the best way?
\begin{itemize}
\item Main program in high-level language with \mac\ subroutines (in \cmkd) 
for specific operations
\item Subroutines are modular, easily debugged, can be reused
\item Percentage of time spent in kernel is minimized; during development,
exception conditions crash only the process
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
What is a \cmkd ?
\begin{itemize}
\item Special type of subroutine library
\item Each routine can be specified to run at a particular access mode
\item When control is transferred back to calling program, {\em returns
to user mode }
\item Also known as user-written system services
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Systems programs which are likely candidates for high-level languages:
\begin{itemize}
\item Ancillary Control Processes (\acp s)
\item Programs which need to run in kernel only briefly, during distinct phases
such as input or output, and most processing can be done in user mode
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Systems programs which are poor candidates for high-level languages:
\begin{itemize}
\item Device drivers and other code loaded into system space
\item Programs which cannot be implemented as a standard VMS process
\item Programs which must run in kernel most of the time
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Advantages of systems programming in \pas:
\begin{itemize}
\item
Programming is easier
\item
Code running in privileged modes is minimized (fewer system crashes). 
\item
All other advantages of high-level languages over MACRO
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Disadvantages of systems programming in \pas:
\begin{itemize}
\item
Code does not track changes to VMS data structures automatically
\item
Cannot be used for device drivers or other code with
limited process context 
\item
Overhead of \cmkd
\end{itemize}
} 
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Topics :
\begin{itemize}
\item Special Requirements of Systems Programming
\item \fbox {The System Control Blocks}
\item A Trivial Example Program 
\item A \pas\ \acp
\item Conclusions
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
System Control Blocks:
\begin{itemize}
\item Defined in \pas\ with standard record mechanism (must be packed records)
\item Referenced with standard \pas\ pointer variables
\item Two types of System Control Blocks---structured and unstructured
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Structured System Control Blocks:
\begin{itemize}
\item Definition of first 11 bytes is the same for all block types
\item First two longwords are forward and backward pointers, used to place
block into a queue
\item Word following queue pointers (offset 8) contains actual length of block
\item Byte following length (offset 10) contains a binary code uniquely 
identifying block type
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
The record layout for the structured xyz block is defined in \pas\ code as:

xyz\_ptr =  \char"5E xyz  ; 

xyz = packed record  \\ 
xyz\_qfl : xyz\_ptr ; \\    
xyz\_qbl : xyz\_ptr ; \\    
xyz\_size : word ; \\    
xyz\_type : byte ; \\    
xyz\_other : byte ; \\    
. \\
. \\
. \\
end ; 
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Unstructured System Control Blocks:
\begin{itemize}
\item Have no header because they are never queued
\item Referenced by other means
\item Length of block fixed or stored outside of block
\item Examples:
\begin{itemize}
\item I/O buffers---address and length are in \irp
\item Process Header (PHD)---length fixed, addressed by index
\end{itemize}
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
The system control block definitions are extracted from the system macro 
library via:

\$ library/macro/extract=\$xyzdef/out=tempfile.dat ---  \\ \hspace*{5em} 
sys\$library:lib
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Before editing:

\$equ pcb\$w\_size 8\\
\$equ pcb\$b\_type 10\\
\$equ pcb\$b\_pri  11\\
\$equ pcb\$l\_pid  96\\

After editing:

pcb\$w\_size : word;\\
pcb\$b\_type : byte;\\
pcb\$b\_pri : byte;\\
pcb\$l\_pid : integer;\\
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Topics :
\begin{itemize}
\item Special Requirements of Systems Programming
\item The System Control Blocks
\item \fbox {A Trivial Example Program}
\item A \pas\ \acp
\item Conclusions
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
SCANPCB---A Trivial Example Program
\begin{itemize}
\item Dumps system control blocks to pas\$output
\item Uses EXAMPSUBS, example \cmkd\ to access nonpaged pool
\item Uses standard \pas\ write lines to dump contents of Process Control Block
(PCB), Process Header (PHD), and Job Information Block (JIB) for current process
to terminal
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
There are three routines contained in the example \cmkd:

\begin{itemize}

\item {\tt GET\_MY\_PCB}, which simply returns the address of the process
control block (PCB) of the current process

\item {\tt COPY\_SYSTEM\_CONTROL\_BLOCK}, copies 
one of the system controls blocks from
the nonpaged-pool to process memory space or vice versa using the lengths
contained within the control blocks themselves---used for structured blocks

\item {\tt COPY\_NONPAGED\_BUFF}, which performs the same kind of a copy operation as 
{\tt COPY\_SYSTEM\_CONTROL\_BLOCK}, except that the lengths are explicitly 
specified---used for unstructured blocks

\end{itemize}

}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
const
   
prv\$v\_cmkrnl = 0  ;\\
prv\$v\_cmexec = 1  ;\\
prv\$v\_sysnam = 2  ;\\
. \\
. \\
. \\
prv\$v\_security = 38  ;
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
type
   
privmask = packed array [0..63] 
               of boolean ; \\
word = 0..65535  ; \\
byte = 0..255  ; \\
pcb\_ptr = \char"5E pcb ; \\
phd\_ptr = \char"5E phd ; \\
jib\_ptr = \char"5E jib ; 
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
var
         
nonpaged\_pcb\_pointer, 
          local\_pcb\_pointer 
          : pcb\_ptr ;

nonpaged\_jib\_pointer, 
          local\_jib\_pointer 
          : jib\_ptr ;

nonpaged\_phd\_pointer,
          local\_phd\_pointer 
          : phd\_ptr ;

privnames : array [prv\$v\_cmkrnl..prv\$v\_security] \\ \hspace*{10em}
          of varying [30] of char ;
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
function copy\_nonpaged\_buff 
          (\%immed fl , tl : integer ; \\ \hspace*{10em}
          \%immed f , t : integer) \\ \hspace*{10em}
          : integer ; extern ;

function copy\_system\_control\_block 
          (\%immed f , t : integer)  \\ \hspace*{10em} : 
          integer ; extern ;

function get\_my\_pcb 
          (var p : pcb\_ptr) 
          : integer ; extern ;
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
procedure init\_priv\_names ;

begin\\
   privnames[prv\$v\_cmkrnl ] 
          := 'cmkrnl' ; \\
   privnames[prv\$v\_cmexec ] 
          := 'cmexec' ; \\
   .\\
   .\\
   .\\
   privnames[prv\$v\_security ] 
          := 'security' ; \\
end ; \\
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
   return\_code := get\_my\_pcb 
          (nonpaged\_pcb\_pointer) ; \\
   new(local\_pcb\_pointer) ; \\
   local\_pcb\_pointer\char"5E .pcb\$w\_size := size(pcb) ; \\
   return\_code := copy\_system\_control\_block \\ \hspace*{10em}
          (nonpaged\_pcb\_pointer :: integer, \\ \hspace*{10em}
          local\_pcb\_pointer :: integer) ; \\
   writeln ('pcb at ' , 
          hex (nonpaged\_pcb\_pointer)) ; \\
   writeln ; \\
   print\_pcb ; \\
   writeln ; \\
   nonpaged\_jib\_pointer := 
          local\_pcb\_pointer\char"5E .pcb\$l\_jib ; \\
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
procedure print\_pcb ;\\
begin\\
   with local\_pcb\_pointer\char"5E\ do begin\\
\hspace*{3em} writeln ('pcb\$l\_sqfl = ' , 
               hex (pcb\$l\_sqfl)) ; \\
\hspace*{3em} writeln ('pcb\$b\_type = ' , 
               pcb\$b\_type) ; \\
\hspace*{3em} .\\
\hspace*{3em} .\\
\hspace*{3em} .\\
\hspace*{3em} writeln ('pcb\$q\_priv = ') ;\\
\hspace*{3em} print\_privs (pcb\$q\_priv) ;\\
\hspace*{3em} writeln ; writeln ; \\
\hspace*{3em} end ; \\
end ; \\
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
procedure print\_privs (privmask : privmask) ;

var
   
   n : integer ;
   
begin \\
   for n := prv\$v\_cmkrnl to prv\$v\_security do \\
\hspace*{3em}       if privmask[n] then \\
\hspace*{6em}          writeln (privnames[n]) ;\\
end ;
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
The following code returns the address of the Process Control Block of
the current process:

pcbaddr = 4                    

\begin{tabular}{lll}
 & \$ent & get\_my\_pcb  \\
 & movl & r4,@pcbaddr(ap)  \\
 & movl & \#ss\$\_normal,r0 \\
55\$: & ret \\
\end{tabular}

Note: the address of the current PCB is preloaded into register 4 by the 
dispatcher; it can also be found via @\#sch\$gl\_curpcb
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
The following code copies a system control block from the process virtual 
address space to system space or vice versa. The lengths of the blocks are
contained within them.

\begin{tabular}{lll}
 & \$ent & copy\_system\_control\_block \\
 & movl & 4(ap),r2 \\
 & movl & 8(ap),r3 \\
 & movc5 & pcb\$w\_size(r2),(r2),\#0,pcb\$w\_size(r3),(r3) \\
 & movl & \#ss\$\_normal,r0 \\
55\$:  & ret & \\
\end{tabular}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Topics :
\begin{itemize}
\item Special Requirements of Systems Programming
\item The System Control Blocks
\item A Trivial Example Program
\item \fbox {A \pas\ \acp }
\item Conclusions
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
What is an \acp\ ?

\begin{itemize}

\item Extension of a device driver

\item Has full process context with regular P0 memory space

\item Can hibernate, wait for event flags, issue system services and perform
lower-level I/O

\item Driver passes QIOs it cannot handle to \acp\ via linked-list (queue) in
nonpaged pool

\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
The functions performed by the ACP include:

\begin{itemize}

\item Creating and initializing the ACP Control Block (AQB) 
and Volume Control Block (VCB) in the non-paged pool.

\item Creating and initializing the Unit Control Block (UCB) 
for each virtual terminal.

\item Reading the I/O Request Packets (\irp s) and converting them to Remote Data Packets (RDPs)
for transmission over the link.

\item Posting the I/O complete when the return RDPs come back from the link

\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
In addition to the two block-copy routines listed above for the example program, 
the following additional routines are contained in the 
\cmkd\ for the \acp:

function allocate\_acp\_queue\_block( var a : aqb\_ptr)\\ \hspace*{10em}
		    : integer ; extern ;

function allocate\_volume\_control\_block( var v : vcb\_ptr)\\ \hspace*{10em}
		    : integer ; extern ;

function get\_irp\_from\_queue( \%immed a : aqb\_ptr; \\ \hspace*{10em}
			var i : irp\_ptr); \\ \hspace*{10em}
		    : integer ; extern ;

}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
More \acp\ dispatcher routines:

function add\_aqb\_to\_system( \%immed a : aqb\_ptr; \\ \hspace*{10em}
			   \%immed v : vcb\_ptr; \\ \hspace*{10em}
			   \%immed u : ucb\_ptr) \\ \hspace*{10em}
		    : integer ; extern ;

function post\_io\_completion( \%immed i : irp\_ptr;\\ \hspace*{10em}
			\%immed u : ucb\_ptr; \\ \hspace*{10em}
		    \%immed rtn : integer)\\ \hspace*{10em}
		    : integer ; extern ;

}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Still more \acp\ dispatcher routines:


function create\_new\_ucb(\%immed tu : ucb\_ptr ; \\ \hspace*{10em}
 var nu : ucb\_ptr)\\ \hspace*{10em}
		    : integer ; extern ;

function kill\_ucb(var nu : ucb\_ptr)\\ \hspace*{10em}
		    : integer ; extern ;



}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
This the the main processing loop of the \acp; during each iteration, it 
either gets another IRP from the \acp\ queue or another RDP returning from
the link.

repeat \\
\hspace*{3em}                io\_performed := false; \\
\hspace*{3em}                if link\_data\_present \\
\hspace*{6em}            	then process\_link\_data; \\
\hspace*{3em}                if odd(get\_irp\_from\_queue \\
\hspace*{9em}                (nonpaged\_aqb\_ptr, \\
\hspace*{9em}                nonpaged\_irp\_ptr)) \\
\hspace*{6em}            	then process\_request\_packet; \\
\hspace*{3em}                if not io\_performed  then \\
\hspace*{6em}            	      hibernate\_until\_new\_input;\\
until dead; \\
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
This section of code saves the essence of the \irp\ in the local address space
and transmits the RDP across the link:

procedure process\_request\_packet; \\
begin \\
io\_performed := true; \\
return\_code :=copy\_system\_control\_block \\
\hspace*{6em}         (nonpaged\_irp\_ptr :: integer; \\
\hspace*{6em}         local\_irp\_ptr :: integer); \\
with local\_irp\_ptr\char"5E\ do begin \\
\hspace*{3em}         from\_ptr := irp\_svapte; to\_ptr := irp\_buff\_ptr; \\
\hspace*{3em}         return\_code := copy\_nonpaged\_buff \\
\hspace*{6em}              (irp\_boff,irp\_boff,from\_ptr,to\_ptr); \\
\hspace*{3em}         put\_irp\_on\_local\_queue(irp\_seqnum); \\
\hspace*{3em}         send\_rdp\_across\_link; \\
\hspace*{3em}         end; \\
end ; 
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
This routine is executed when the RDP returns from the link and the original
QIO is posted complete.

procedure process\_io\_completion;

begin \\
locate\_saved\_irp; \\
with local\_irp\_ptr\char"5E\  do begin \\
\hspace*{3em}         from\_ptr := inbuff; to\_ptr := irp\_svapte ;  \\
\hspace*{3em} 	      return\_code := copy\_nonpaged\_buff(inbuff\_length ,\\
\hspace*{6em}         inbuff\_length , from\_ptr, to\_ptr); \\
\hspace*{3em}         return\_code := post\_io\_completion(nonpaged\_irp\_ptr, \\
\hspace*{6em}         nonpaged\_ucb,interrupt\_routine\_address); \\
\hspace*{3em}         end; \\
end ;  
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
This section of code allocates and initializes an \acp\ Queue Block (AQB).

return\_code :=allocate\_acp\_queue\_block(nonpaged\_aqb\_ptr);

new(local\_aqb\_ptr); \\
with local\_aqb\_ptr\char"5E\  do \\
\hspace*{3em}            begin \\
\hspace*{3em}            aqb\_size := aqb\$c\_length; \\
\hspace*{3em}            aqb\_type := dyn\$c\_aqb; \\
\hspace*{3em}            aqb\_mntcnt := 0; \\
\hspace*{3em}            aqb\_acptype := aqb\$k\_rem; \\
\hspace*{3em}            aqb\_acppid := my\_pid; \\
\hspace*{3em}            end; \\
return\_code :=copy\_system\_control\_block(local\_aqb\_ptr :: integer; \\
\hspace*{6em}         			nonpaged\_aqb\_ptr :: integer); 
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
The following code allocates an AQB in the nonpaged pool, via the 
allocate\_nonpaged procedure:

\begin{tabular}{lll}
 &	\$ent &	allocate\_acp\_queue\_block,0		 \\
 & 	pushl &	4(ap)			 \\
 & 	pushl &	\#aqb\$c\_length		 \\
 & 	calls &	\#2,allocate\_nonpaged	 \\
 &	blbc  & r0,10\$ \\
 &	movl  & @4(ap),r1	\\
 & 	movw  & \#aqb\$c\_length,aqb\$w\_size(r1) \\
10\$: & 	ret 
\end{tabular}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
The following code returns the address of the next IRP in the \acp\ queue, if
one is present; if the queue is empty, a warning-level return code is issued.

\begin{tabular}{lll}
 &	\$ent &	get\_irp\_from\_queue \\
 & 	movl &	4(ap),r5		 \\
 & 	beql &	59\$			 \\
 & 	remque &	@(r5),@8(ap)		 \\
 & 	bvc &	10\$			 \\
59\$: &	movl &	\#queue\_is\_empty,r0		 \\
 & 	ret \\
10\$: & movl &	\#ss\$\_normal,r0		 \\
 & 	ret \\
\end{tabular}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Topics :
\begin{itemize}
\item Special Requirements of Systems Programming
\item The System Control Blocks
\item A Trivial Example Program 
\item A \pas\ \acp
\item \fbox {Conclusions}
\end{itemize}
}
\end{slide}
\begin{slide}{} \parbox{\textwidth}{
Conclusions:
\begin{itemize}
\item Some classes of systems programs can be implemented in a high-level
language
\item Systems programs can be made more readable
\item Development time can be shortened 
\item System crashes can be reduced during development
\item Kernel mode routines (in a \cmkd) developed for one system program can
be used again for another
\end{itemize}
}
\end{slide}
