
	.TITLE	DEVWATCH "Device monitor"
	.IDENT /v1.0/
;+
; Facility:
;	DEVWATCH.MAR - Watch a device
;
; Abstract:
;	The program will replace a device's entry points
;
;	The idea was to write something like "SET WATCH FILE" which
;	logged arbitrary $QIO calls instead of F11 XPQ functions.
;	This program should (ha ha!) intercept any requests for the
;	given device, as opposed to JAMON which can only catch calls
;	originating from a specific process context.
;
; Author:
;	Bruce R. Miller, MILLER@TGV.COM
;	TGV, Inc.
;	603 Mission St.
;	Santa Cruz, CA 95060
;	(408) 427-4366
;
; Date:
;	2-JUN-1991, late at night
;
; Copyright (c) 1991 Bruce R. Miller
; All rights reserved.
;
;	Redistribution and use in source and binary forms are permitted
;	provided that the above copyright notice and this paragraph are
;	duplicated in all such forms and that any documentation,
;	advertising materials, and other materials related to such
;	distribution and use acknowledge that the software was developed
;	by Bruce R. Miller.
;	THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR
;	IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
;	WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
;
; Modifications:
;
;-

	.link		"sys$system:sys.stb"/SELECTIVE_SEARCH
	.library	"sys$Library:lib.mlb"

	$CCBDEF
	$DDBDEF
	$DDTDEF
	$DYNDEF
	$IODEF
	$IPLDEF
	$IRPDEF
	$PCBDEF
	$PSLDEF
	$PTEDEF
	$SSDEF
	$UCBDEF

;
; Hardwired offsets into the IOC$IOPOST
;
IOC_VECTOR	= ^x90		; IOC$IOPOST (IPL 4 interrupt) offset in SCB
; IOPOST is at the top of the IRP processing loop, after the register
; saving code.
IOPOST		= ^x09		; Offset into IOC$IOPOST
; IOC_BYPASS is the point where we patch the IPL interrupt handler
IOC_BYPASS	= ^x60		; Offset into IOC$IOPOST



;
; DevWatch Message Block definition
;
;	Message blocks are used store event information
;	until it can be printed.
;

	$DEFINI DWMSG
$DEF	DWMsg_L_FLink		.BLKL 1
$DEF	DWMsg_L_BLink		.BLKL 1
$DEF	DWMsg_W_Size		.BLKW 1
$DEF	DWMsg_B_Type		.BLKB 1
$DEF	DWMsg_B_Class		.BLKB 1

$DEF	DWMsg_Q_TIME		.BLKQ 1

$DEF	DWMsg_L_PID		.BLKL 1
$DEF	DWMsg_L_IRP		.BLKL 1
$DEF	DWMsg_L_UCB		.BLKL 1
$DEF	DWMsg_L_Func		.BLKL 1

$DEF	DWMsg_L_IOST1		.BLKL 1
$DEF	DWMsg_L_IOST2		.BLKL 1

$DEF	DWMsg_L_P1		.BLKL 1
$DEF	DWMsg_L_P2		.BLKL 1
$DEF	DWMsg_L_P3		.BLKL 1
$DEF	DWMsg_L_P4		.BLKL 1
$DEF	DWMsg_L_P5		.BLKL 1
$DEF	DWMsg_L_P6		.BLKL 1

$DEF	DWMsg_C_Length				; Length of a message
	$DEFEND DWMSG

;
; DevWatch message classes
;
DW_C_NULL	= 0			; Null message
DW_C_FDT	= 1			; Intercepted FDT routines
DW_C_START	= 2			; Intercepted DDT Start I/O
DW_C_ALT	= 3			; Intercepted DDT AltStart I/O
DW_C_CANCEL	= 4			; Intercepted DDT Cancel
DW_C_IOPOST	= 5			; Intercepted IOC$IOPOST stuff


.PSECT KData,QUAD,PIC		; Quad-alignment
dbase:

;
; Standard system block header
;
d_SysQHead	= . - dbase
dQHead:		.BLKQ	1

d_SysSize	= . - dbase
dSysSize:	.BLKW	1

d_SysType	= . - dbase
dSysType:	.BLKB	1

d_SysSpare	= . - dbase
dSysSpare:	.BLKB	1

;
; Fake FDT - replaces the device's real Function Dispatch Table
;

; Legal function mask
d_LegalFunc	= . - dbase
dLegalFunc:		.BLKQ	1

; Buffered I/O function mask
d_BufFunc	= . - dbase
dBufFunc:		.BLKQ	1

; Fake function mask
d_FakeFuncMask	= . - dbase
dFakeFuncMask:		.BLKQ	1

; Fake function routine
d_FakeFuncRtn	= . - dbase
dFakeFuncRtn:		.BLKL	1

; NB: d_PendQ must be a multiple of 8 for queue alignment

; Self-relative queue of message blocks awaiting processing
.ALIGN	QUAD
d_PendQ		= . - dbase
dPendQ:			.BLKQ	1

; Self-relative queue of message blocks available for sending
d_FreeQ		= . - dbase
dFreeQ:			.BLKQ	1

; Number of message blocks allocated so far
d_FreeSize	= . - dbase
dFreeSize:		.BLKL	1

; Text field
d_Text = . - dbase
dText:			.BLKL	4

; Count of FDT calls
d_Count_FDT = . - dbase
dCount_FDT:		.BLKL	1

; Count of StartIO calls
d_Count_Start = . - dbase
dCount_Start:		.BLKL	1

; Count of AltStart calls
d_Count_Alt = . - dbase
dCount_Alt:		.BLKL	1

; Count of Cancel requests
d_Count_Cancel = . - dbase
dCount_Cancel:		.BLKL	1

; Count of IOPost completions
d_Count_IOPost = . - dbase
dCount_IOPost:		.BLKL	1

; Internal PID of originating process
d_IPID = . - dbase
dIPID:			.BLKL	1

; PID of originating process
d_Target_PID = . - dbase
dTarget_PID:		.BLKL	1

; DDB of device being watched
d_Target_DDB = . - dbase
dTarget_DDB:		.BLKL	1

; Flags
d_Flags = . - dbase
dFlags:			.BLKL	1

; Scratch location
d_Scratch = . - dbase
dScratch:		.BLKL	1

; Debug info longword #1
d_Debug1 = . - dbase
dDebug1:		.BLKL	1

; Address of this device's original FDT
d_RealFDT = . - dbase
dRealFDT:		.BLKL	1

; Address of this device's original ALTSTART routine
d_RealAltStart = . - dbase
dRealAltStart:		.BLKL	1

; Address of this device's original STARTIO routine
d_RealStartIO = . - dbase
dRealStartIO:		.BLKL	1

; Address of this device's original CANCEL routine
d_RealCancel = . - dbase
dRealCancel:		.BLKL	1

; Address of this machine's original IOCIOPOST IPL 4 interrupt vector
d_RealIOPOST = . - dbase
dRealIOPOST:		.BLKL	1

; Save patched section of IOC
d_IOC_Patch = . - dbase
dIOC_Patch:		.BLKQ	1

; 
d_RealIOC_Bypass = . - dbase
dRealIOC_Bypass:	.BLKL	1

; Start of the replacement code
d_Code = .- dbase
dCode:

;++
;	Get_MsgBlk - Get a free message block, allocate it if neccessary
;
; Input:	R9 - DevWatch Database
;
; Output:	R0 - Status
;		R1 - destroyed
;		R2 - Address of free message block
;--
Get_MsgBlk:
;HACK!!! do we need to save these registers?
	PUSHR	#^M<R3,R4,R5>
	MOVL	#SS$_NORMAL,R0
	REMQHI	d_FreeQ(R9),R2			; Get a msg blk from free queue
	BVC	10$				; br is sucess
	MOVL	#DWMsg_C_Length,R1			; Get some npagdyn
	JSB	G^EXE$ALONONPAGED		;   for a message block
	BLBC	R0,10$				; If failed exit error
	MOVW	R1,DWMsg_W_Size(R2)		; Fill in size field
	MOVB	#DYN$C_NON_PAGED,DWMsg_B_Type(R2) ; Set the type
	INCL	d_FreeSize(R9)			; Up the allocated count
10$:	POPR	#^M<R3,R4,R5>
	RSB

;++
;	Dispatch_Msg - Queue a message and wake the server
;
; Input:	R2 - Message Block
;		R9 - DevWatch Database
;
; Output:	R0 - Status
;		R1 - ???
;
; NB: We only need to call sch$wake if there are no other messages in queue
;--
Dispatch_Msg:
;HACK!!! do we need to save these registers?
	PUSHR	#^M<R3,R4,R5>

	; Time stamp (from system quad time)
	MOVQ	G^EXE$GQ_SYSTIME,DWMsg_Q_TIME(R2)

	; Insert message into end of database queue
	INSQTI	(R2),d_PendQ(R9)
	ADAWI	#1,d_Scratch(R9)		; update # outstanding

;HACK!!! - is there a better way that to call SCH$WAKE?
;HACK!!! - Do we need to call wake if there is already something in the queue?
	; Lock the scheduler database
	LOCK	LOCKNAME=SCHED,-		; Lock the scheduler
		SAVIPL=-(SP),-			; Save IPL on stack
		PRESERVE=NO			; Waste R0

	; Schedule a wakeup for the message printer
	MOVL	d_IPID(R9),R1			; push PID
	JSB	G^SCH$WAKE			; Wake up monitor process
						; Ignore status code for now
	; Unlock the scheduler database
	UNLOCK	LOCKNAME=SCHED,-		; Release SCHED lock
		NEWIPL=(SP)+,-			; Pop new IPL off the stack
		PRESERVE=NO			; Waste R0 again

	; Return to caller
	POPR	#^M<R3,R4,R5>
	RSB

;++
;	FDT - A fake FDT called from EXE$QIOREQ
;
; Input:
;
;	R0 = Address of FDT routine entry point
;	R3 = I/O Packet address
;	R4 = Current Process Control Block address
;	R5 = Unit Control Block Address
;	R6 = Channel Control Block Address
;	R7 = Function Code Bit Number
;	R8 = Address of the FDT table entry for this routine
;	R9-R11 = Scratch
;	P1-P6 function specific parameters
;
; Output:
;	none
;--
.ALIGN	LONG
d_FDT = .- dbase
dFDT:
	MOVAB	dbase,R9			; Start of our S0 data
	INCL	d_Count_FDT(R9)			; Increment the operation count

	; Get a message block
	JSB	Get_MsgBlk			; Get a free message block
	BLBC	R0,100$				; br if none available
	MOVB	#DW_C_FDT,DWMsg_B_Class(R2)	; Set the class to FDT

10$:	; Fill in Message
	MOVL	PCB$L_PID(R4),DWMsg_L_PID(R2)	; requesting process ID
	MOVL	R3,DWMsg_L_IRP(R2)		; IRP
	MOVL	R5,DWMsg_L_UCB(R2)		; Target UCB
	
	MOVL	0(AP),DWMsg_L_P1(R2)		; P1
	MOVL	4(AP),DWMsg_L_P2(R2)		; P2
	MOVL	8(AP),DWMsg_L_P3(R2)		; P3
	MOVL	12(AP),DWMsg_L_P4(R2)		; P4
	MOVL	16(AP),DWMsg_L_P5(R2)		; P5
	MOVL	20(AP),DWMsg_L_P6(R2)		; P6

	MOVZWL	IRP$W_FUNC(R3),DWMsg_L_Func(R2)	; Function code

	PUSHR	#^M<R2,R9>			; Save Registers
	CALLG	(AP),Dispatch_FDT
	MOVL	R0,R10
	POPR	#^M<R2,R9>

	MOVL	R10,DWMsg_L_IOST1(R2)		; Status code

	; Queue message to the monitor
	JSB	Dispatch_Msg

100$:	RET

.entry Dispatch_FDT,^M<>
; Dispatch the request through the real FDT
	MOVAL	dRealFDT,R8
	MOVL	(R8),R8			; get start of FDT
	ADDL	#8+8,R8			; skip FDT header
110$:	; top of loop
	BBC	R7,(R8),120$		; function bit set in mask?
	MOVL	8(R8),R0		; get routine address
	JSB	(R0)			; call the real FDT action
120$:	; go to next FDT entry
	ADDL	#12,R8
	BRB	110$			; Loop forever


;++
;	AltStart - A fake AltStart routine
;
; Input:
; Output:
;--
.ALIGN	LONG
d_AltStart = .- dbase
dAltStart:
	PUSHL	R9				; Save Registers
	MOVAB	dbase,R9			; Start of our S0 data
	INCL	d_Count_Alt(R9)			; Increment the operation count

	; Get a message block
	JSB	Get_MsgBlk			; Get a free message block
	BLBC	R0,100$				; br if none available
	MOVB	#DW_C_ALT,DWMsg_B_Class(R2)	; Set the class to ALTSTART

10$:	; Fill in Message
	MOVL	IRP$L_PID(R3),DWMsg_L_PID(R2)	; requesting process ID
	MOVL	R3,DWMsg_L_IRP(R2)		; IRP
	MOVL	R5,DWMsg_L_UCB(R2)		; Target UCB
	MOVZWL	IRP$W_FUNC(R3),DWMsg_L_Func(R2)	; Function code

	; Queue message to the monitor
	JSB	Dispatch_Msg

100$:	; get the real AltStart address
	MOVL	d_RealAltStart(R9),R0

	; restore registers
	POPL	R9

	; Dispatch the request through the real ALTSTART routine
	JMP	(R0)



;++
;	StartIO - A fake Start I/O routine
;
; Input:
; Output:
;--
.ALIGN	LONG
d_StartIO = .- dbase
dStartIO:
	PUSHL	R9				; Save Registers
	MOVAB	dbase,R9			; Start of our S0 data
	INCL	d_Count_Start(R9)		; Increment the operation count

	; Get a message block
	JSB	Get_MsgBlk			; Get a free message block
	BLBC	R0,100$				; br if none available
	MOVB	#DW_C_START,DWMsg_B_Class(R2)	; Set the class to ALTSTART

10$:	; Fill in Message
	MOVL	IRP$L_PID(R3),DWMsg_L_PID(R2)	; requesting process ID
	MOVL	R3,DWMsg_L_IRP(R2)		; IRP
	MOVL	R5,DWMsg_L_UCB(R2)		; Target UCB
	MOVZWL	IRP$W_FUNC(R3),DWMsg_L_Func(R2)	; Function code

	; Queue message to the monitor
	JSB	Dispatch_Msg

100$:	; get the real StartIO address
	MOVL	d_RealStartIO(R9),R0

	; restore registers
	POPL	R9
	; Dispatch the request through the real STARTIO routine
	JMP	(R0)


;++
;	Cancel - A fake Cancel routine
;
; Input:
; Output:
;--
.ALIGN	LONG
d_Cancel = .- dbase
dCancel:
	PUSHL	R9				; Save Registers
	MOVAB	dbase,R9			; Start of our S0 data
	INCL	d_Count_Cancel(R9)			; Increment the operation count

	; Get a message block
	JSB	Get_MsgBlk			; Get a free message block
	BLBC	R0,100$				; br if none available
	MOVB	#DW_C_CANCEL,DWMsg_B_Class(R2)	; Set the class to CANCEL

10$:	; Fill in Message
	MOVL	PCB$L_PID(R4),DWMsg_L_PID(R2)	; requesting process ID
	MOVL	R3,DWMsg_L_IRP(R2)		; IRP
	MOVL	R5,DWMsg_L_UCB(R2)		; Target UCB
	MOVL	R8,DWMsg_L_Func(R2)		; Reason code

	; Queue message to the monitor
	JSB	Dispatch_Msg

100$:	; get the real Cancel address
	MOVL	d_RealCancel(R9),R0

	; restore registers
	POPL	R9
	; Dispatch the request through the real CANCEL routine
	JMP	(R0)


;++
;	dIOPOST - fake IOC$IOPOST interrupt vector
;
; Input:
; Output:
;
; Assumptions:
;	(From VAX/VMS Internals and Data Structures by Ruth E. Goldenburg
;	 and Lawrence J. Kenah)
;
;	IOC$IOPOST::				; IOPOST interrupt
;		MOVQ	R4,-(SP)		; Save
;		MOVQ	R2,-(SP)		;  normal
;		MOVQ	R0,-(SP)		;  registers (R0-R5)
;	IOPOST:
;		FIND_CPU_DATA R1, ISTACK=YES	; Get addr of per-CPU database
;		CMPL	CPU$L_PHY_CPUID(R1),G^SMP$GL_PRIMID
;						; Are we the primary?
;		BNEQ	5$
;		TSTL	G^IOC$GQ_POSTIQ,R5	; Is systemwide queue empty?
;		BEQL	5$			; branch if yes, service
;						;   per-CPU queue
;		$REMQHI	G^IOC$GQ_POSTIQ,R5	; Remove next packet
;		BVC	60$			; Branch if got one
;	5$:	REMQUE	@CPU$L_PSFL(R1),R5	; Remove next packet
;		BVC	60$			; Branch if got one
;		MOVQ	(SP)+,R0		; Restore
;		MOVQ	(SP)+,R2		;  normal
;		MOVQ	(SP)+,R4		;  registers (R0-R5)
;		REI				; return from interrupt
;	IOC_BYPASS:
;	60$:	; All IRPs pass here (offset from IOC$IOPOST is ^x60!)
;		MOVL	IRP$L_PID(R5),R1
;		BLSS	blah
;	61$:	; continue (offset from IOC$IOPOST id ^x66)
;	[...]
;--
.ALIGN	LONG
d_IOPOST = .- dbase
dIOPOST:

;HACK!!! do we need to save all these registers?
	PUSHR	#^M<R2,R3,R4,R5,R9>		; Save Register
	MOVAB	dbase,R9			; Start of our S0 data
	INCL	d_Count_IOPost(R9)		; Increment the operation count
	MOVL	IRP$L_UCB(R5),R3		; Get UCB
	BEQL	90$				; br if no UCB
	CMPL	UCB$L_DDB(R3),d_Target_DDB(R9)	; Right device?
	BNEQ	90$				; wrong device

	; Get a message block
	JSB	Get_MsgBlk			; Get a free message block
	BLBC	R0,100$				; br if none available
	MOVB	#DW_C_IOPOST,DWMsg_B_Class(R2)	; Set the class to ALTSTART

	; Fill in Message
	MOVL	R5,DWMsg_L_IRP(R2)		; IRP
	MOVL	IRP$L_PID(R5),DWMsg_L_PID(R2)	; PID
	MOVL	IRP$L_UCB(R5),DWMsg_L_UCB(R2)	; UCB
	MOVL	IRP$L_IOST1(R5),DWMsg_L_IOST1(R2)	; IOSB part 1
	MOVL	IRP$L_IOST2(R5),DWMsg_L_IOST2(R2)	; IOSB part 2

	; Queue message to the monitor
	JSB	Dispatch_Msg

90$:	; Restore registers
	POPR	#^M<R2,R3,R4,R5,R9>		; Save Register

100$:	; perform instructions replaced by out patch.  (SP) is return PC.
	MOVL	IRP$L_PID(R5),R1		; Get "PID"
	BGEQ	120$				; If S0 addr,
	JSB	(R1)				;   call routine
;HACK - combine next two statements
	SUBL	#IOC_BYPASS+6,(SP)		; Simulate JMP IOPOST
	ADDL	#IOPOST,(SP)			;   return to start of IOPOST
120$:	RSB					; Continue with IOC$IOPOST



;++
;	dIOPOST - fake IOC$IOPOST interrupt vector
;
; Input:
; Output:
;--
.IF	DEFINED notdef
.ALIGN	LONG
d_IOPOST2 = .- dbase
dIOPOST2:
	CLRL	-(SP)				; Allocate longwork from stack
	PUSHQ	R0				; Save R0 and R1
	MOVL	G^IOC$GQ_POSTIQ,R0		; Get offset to first element
	BEQL	100$				; Leave if nothing there.
BRB	100$
;HACK!!! do we need to save all these registers?
	PUSHR	#^M<R2,R3,R4,R5,R9>		; Save Register
	MOVAB	dbase,R9			; Start of our S0 data
	INCL	d_Count_IOPost(R9)		; Increment the operation count
	ADDL3	#IOC$GQ_POSTIQ,R0,R5		; Get IRP address
	MOVL	R5,d_Debug1(R9)
	MOVL	IRP$L_UCB(R5),R3		; Get UCB
	CMPL	UCB$L_DDB(R3),d_Target_DDB(R9)	; Right device?
	BNEQ	90$				; wrong device

	; Get a message block
	JSB	Get_MsgBlk			; Get a free message block
	BLBC	R0,100$				; br if none available
	MOVB	#DW_C_IOPOST,DWMsg_B_Class(R2)	; Set the class to ALTSTART

	; Fill in Message
	MOVL	R3,DWMsg_L_IRP(R2)		; IRP
	MOVL	IRP$L_IOST1(R3),DWMsg_L_P1(R2)	; IOSB part 1
	MOVL	IRP$L_IOST2(R3),DWMsg_L_P2(R2)	; IOSB part 2

	; Queue message to the monitor
	JSB	Dispatch_Msg

90$:	; Restore registers
	POPR	#^M<R2,R3,R4,R5,R9>		; Save Register

100$:	; get the real IOPOST address
	MOVAB	dbase,R0			; Start of our S0 data
	MOVL	d_RealIOPOST(R0),8(SP)		; fake RSB address
	BICL	#3,8(SP)			; longword align
	ADDL	#IOC_BYPASS,8(SP)
	POPQ	R0				; Restore R0 and R1

	; Dispatch the interrupt through the real vector (on the stack)
	RSB
.ENDC



dcode_len = . - dcode
dlen = . - dbase



.PSECT		; Resume using the default PSECT

; A quad to describe the rest of the code segment.  Used by $LKWSET call.
Locked_Pages:
	.LONG	First_Page
	.LONG	Last_Page
First_Page::	; Begining of locked page range


command_chan:	.BLKW	1

CR = 10
LF = 13

done_flag:	.BLKL	1
my_pid:		.BLKL	1

bsize = 1
buffer:		.BLKB	1

obsize = 512
obuff:		.BLKB	obsize

; Device to get input from
Command_Desc:
	.ASCID	/SYS$COMMAND/

;
; Descriptors used to format monitor messages.
;
NULL_Message_Desc:
	.ASCID	<CR><LF>"!%T NULL: R2=!XL"<LF>
FDT_Message_Desc:
	.ASCID	<CR><LF>"!%T FDT: PID !XL, IRP !XL, UCB !XL, Func !XW!AC"<CR><LF>"            Arg (!XL,!XL,!XL,!XL,!XL,!XL) => !XW"<LF>
ALT_Message_Desc:
	.ASCID	<CR><LF>"!%T ALT: PID !XL  IRP !XL  UCB !XL  Func !XW!AC"<LF>
START_Message_Desc:
	.ASCID	<CR><LF>"!%T START: PID !XL IRP !XL UCB !XL Func !XW!AC"<LF>
CANCEL_Message_Desc:
	.ASCID	<CR><LF>"!%T CANCEL: PID !XL, IRP !XL, UCB !XL, Reason !XB"<LF>
IOPOST_Message_Desc:
	.ASCID	<CR><LF>"!%T IOPOST: PID !XL, IRP !XL, IOSB (!XL,!XL)"<LF>

;
; Strings representing the various possible $QIO funcitons (from $IODEF)
;
QIO_NULL_Str:		.ASCIC ""
QIO_NOP_Str:		.ASCIC "(NOP)"
QIO_UNLOAD_Str:		.ASCIC "(UNLOAD)"
QIO_SEEK_Str:		.ASCIC "(SEEK)"
QIO_RECAL_Str:		.ASCIC "(RECAL)"
QIO_DRVCLR_Str:		.ASCIC "(DRVCLR)"
QIO_RELEASE_Str:	.ASCIC "(RELEASE)"
QIO_OFFSET_Str:		.ASCIC "(OFFSET)"
QIO_QSTOP_Str:		.ASCIC "(QSTOP)"
QIO_PACKACK_Str:	.ASCIC "(PACKACK)"
QIO_SEARCH_Str:		.ASCIC "(SEARCH)"
QIO_WRITECHECK_Str:	.ASCIC "(WRITECHECK)"
QIO_WRITEPBLK_Str:	.ASCIC "(WRITEPBLK)"
QIO_READPBLK_Str:	.ASCIC "(READPBLK)"
QIO_WRITEHEAD_Str:	.ASCIC "(WRITEHEAD)"
QIO_READHEAD_Str:	.ASCIC "(READHEAD)"
QIO_WRITETRACKD_Str:	.ASCIC "(WRITETRACKD)"
QIO_READTRACKD_Str:	.ASCIC "(READTRACKD)"
QIO_AVAILABLE_Str:	.ASCIC "(AVAILABLE)"
QIO_SETPRFPATH_Str:	.ASCIC "(SETPRFPATH)"
QIO_DISPLAY_Str:	.ASCIC "(DISPLAY)"
QIO_NONE1_Str:		.ASCIC ""
QIO_DSE_Str:		.ASCIC "(DSE)"
QIO_REREADN_Str:	.ASCIC "(REREADN)"
QIO_REREADP_Str:	.ASCIC "(REREADP)"
QIO_WRITERET_Str:	.ASCIC "(WRITERET)"
QIO_READPRESET_Str:	.ASCIC "(READPRESET)"
QIO_SETCHAR_Str:	.ASCIC "(SETCHAR)"
QIO_SENSECHAR_Str:	.ASCIC "(SENSECHAR)"
QIO_WRITEMARK_Str:	.ASCIC "(WRITEMARK)"
QIO_DIAGNOSE_Str:	.ASCIC "(DIAGNOSE)"
QIO_FORMAT_Str:		.ASCIC "(FORMAT)"
QIO_PHYSICAL_Str:	.ASCIC "(PHYSICAL)"
QIO_WRITELBLK_Str:	.ASCIC "(WRITELBLK)"
QIO_READLBLK_Str:	.ASCIC "(READLBLK)"
QIO_REWINDOFF_Str:	.ASCIC "(REWINDOFF)"
QIO_SETMODE_Str:	.ASCIC "(SETMODE)"
QIO_REWIND_Str:		.ASCIC "(REWIND)"
QIO_SKIPFIL_Str:	.ASCIC "(SKIPFIL)"
QIO_SKIPRECORD_Str:	.ASCIC "(SKIPRECORD)"
QIO_SENSEMODE_Str:	.ASCIC "(SENSEMODE)"
QIO_WRITEOF_Str:	.ASCIC "(WRITEOF)"
QIO_TTY_PORT_Str:	.ASCIC "(TTY_PORT)"
QIO_FLUSH_Str:		.ASCIC "(FLUSH)"
QIO_READLCHUNK_Str:	.ASCIC "(READLCHUNK)"
QIO_WRITELCHUNK_Str:	.ASCIC "(WRITELCHUNK)"
QIO_NONE2_Str:		.ASCIC ""
QIO_NONE3_Str:		.ASCIC ""
QIO_LOGICAL_Str:	.ASCIC "(LOGICAL)"
QIO_WRITEVBLK_Str:	.ASCIC "(WRITEVBLK)"
QIO_READVBLK_Str:	.ASCIC "(READVBLK)"
QIO_ACCESS_Str:		.ASCIC "(ACCESS)"
QIO_CREATE_Str:		.ASCIC "(CREATE)"
QIO_DEACCESS_Str:	.ASCIC "(DEACCESS)"
QIO_DELETE_Str:		.ASCIC "(DELETE)"
QIO_MODIFY_Str:		.ASCIC "(MODIFY)"
QIO_READPROMPT_Str:	.ASCIC "(READPROMPT)"
QIO_ACPCONTROL_Str:	.ASCIC "(ACPCONTROL)"
QIO_MOUNT_Str:		.ASCIC "(MOUNT)"
QIO_TTYREADALL_Str:	.ASCIC "(TTYREADALL)"
QIO_TTYREADPALL_Str:	.ASCIC "(TTYREADPALL)"
QIO_CONINTREAD_Str:	.ASCIC "(CONINTREAD)"
QIO_CONINTWRITE_Str:	.ASCIC "(CONINTWRITE)"
QIO_NONE4_Str:		.ASCIC ""
QIO_VIRTUAL_Str:	.ASCIC "(VIRTUAL)"

;
; List of string addresses used to do func_code to string translations
;
Func_Tab:	.ADDRESS -
	QIO_NOP_Str, QIO_UNLOAD_Str, QIO_SEEK_Str, QIO_RECAL_Str,-
	QIO_DRVCLR_Str, QIO_RELEASE_Str, QIO_OFFSET_Str, QIO_QSTOP_Str,-
	QIO_PACKACK_Str, QIO_SEARCH_Str, QIO_WRITECHECK_Str,QIO_WRITEPBLK_Str,-
	QIO_READPBLK_Str, QIO_WRITEHEAD_Str, QIO_READHEAD_Str, -
	QIO_WRITETRACKD_Str, QIO_READTRACKD_Str, QIO_AVAILABLE_Str,-
	QIO_SETPRFPATH_Str, QIO_DISPLAY_Str, QIO_NONE1_Str, QIO_DSE_Str,-
	QIO_REREADN_Str, QIO_REREADP_Str, QIO_WRITERET_Str,-
	QIO_READPRESET_Str, QIO_SETCHAR_Str, QIO_SENSECHAR_Str,-
	QIO_WRITEMARK_Str, QIO_DIAGNOSE_Str, QIO_FORMAT_Str, QIO_PHYSICAL_Str,-
	QIO_WRITELBLK_Str, QIO_READLBLK_Str, QIO_REWINDOFF_Str,-
	QIO_SETMODE_Str, QIO_REWIND_Str, QIO_SKIPFIL_Str, QIO_SKIPRECORD_Str,-
	QIO_SENSEMODE_Str, QIO_WRITEOF_Str, QIO_TTY_PORT_Str, QIO_FLUSH_Str,-
	QIO_READLCHUNK_Str, QIO_WRITELCHUNK_Str, QIO_NONE2_Str, QIO_NONE3_Str,-
	QIO_LOGICAL_Str, QIO_WRITEVBLK_Str, QIO_READVBLK_Str, QIO_ACCESS_Str,-
	QIO_CREATE_Str, QIO_DEACCESS_Str, QIO_DELETE_Str, QIO_MODIFY_Str,-
	QIO_READPROMPT_Str, QIO_ACPCONTROL_Str, QIO_MOUNT_Str,-
	QIO_TTYREADALL_Str, QIO_TTYREADPALL_Str, QIO_CONINTREAD_Str,-
	QIO_CONINTWRITE_Str, QIO_NONE4_Str, QIO_VIRTUAL_Str

;++
;	TT_Input - AST routine for SYS$COMMAND read completion
;
;	If there is any input, exit the program.
;--
.entry TT_Input,^M<R3,R4>

;	BLBS	IOSB,10$
;	CMPW	IOSB,#SS$_CANCEL

10$:	; Tell the main code thread that we are done
	MOVL	#1,done_flag

	; Wake up this process
	CLRL	-(SP)
	CLRL	-(SP)
	CALLS	#2,G^SYS$WAKE

	RET

;++
;	Print - combined FAO and QIO
;
; Call sequence:  CALL Print( chan, cstr, p1,p2...)
;--
.entry Print,^M<R3,R4>

	; Format the string
	PUSHAB	obuff				; outbuf desc.pointer
	PUSHL	#obsize				; outbuf desc.size
	MOVAL	-(SP),R3			; retlen pointer

	MOVAL	12(AP),-(SP)			; prmlst
	MOVAL	4(R3),-(SP)			; outbuf descriptor
	PUSHL	R3				; outlen
	PUSHL	8(AP)				; cstr
	CALLS	#4,G^SYS$FAOL			; print the message
	POPL	R3				; Get outlen
	MOVZWL	R3,R3				; Get low word of outlen
	ADDL	#8,SP				; pop descriptor off stack
	BLBC	R0,100$				; If failed exit error

	; Send data
	CLRQ	-(SP)				; P5 / P6
	CLRQ	-(SP)				; P3 / P4
	PUSHL	R3				; P2 == outlen
	PUSHAL	obuff				; P1 == outbuf
	CLRQ	-(SP)				; ASTADR / ASTPRM
	CLRL	-(SP)				; IOSB
	PUSHL	#IO$_WRITEVBLK			; Func
	PUSHL	4(AP)				; chan
	PUSHL	#0				; EFN
	CALLS	#12,G^EXE$QIO

100$:	RET


;++
;	Print_Message
;
; Call sequence:  CALL Print_Message( chan, MsgBlk )
;--
.entry Print_Message,^M<R2,R3,R4>
	MOVL	8(AP),R2			; Get message block

100$:	; Print STARTIO messages
	CMPB	DWMsg_B_Class(R2),#DW_C_START	; Is it a StartIO message?
	BNEQ	200$				; br if not

	PUSHL	DWMsg_L_Func(R2)		; Push the func for JSB
	JSB	Get_QIO_funtion_str		; Put ASCIC string in R1
	MOVL	R1,(SP)				; Put string on stack
	PUSHL	DWMsg_L_Func(R2)		; Push the function code
	PUSHL	DWMsg_L_UCB(R2)			; Push the UCB
	PUSHL	DWMsg_L_IRP(R2)			; Push the IRP
	PUSHL	DWMsg_L_PID(R2)			; Push the PID
	PUSHAL	DWMsg_Q_TIME(R2)		; current time
	PUSHAB	START_Message_Desc		; Push message descriptor
	PUSHL	command_chan			; Output channel
	CALLS	#8,Print			; output a message
	BRW	1000$				; Leave

200$:	; Print ALTSTART messages
	CMPB	DWMsg_B_Class(R2),#DW_C_ALT	; Is it an AltStart message?
	BNEQ	300$				; br if not

	PUSHL	DWMsg_L_Func(R2)		; Push the func for JSB
	JSB	Get_QIO_funtion_str		; Put ASCIC string in R1
	MOVL	R1,(SP)				; Put string on stack
	PUSHL	DWMsg_L_Func(R2)		; Push the function code
	PUSHL	DWMsg_L_UCB(R2)			; Push the UCB
	PUSHL	DWMsg_L_IRP(R2)			; Push the IRP
	PUSHL	DWMsg_L_PID(R2)			; Push the PID
	PUSHAL	DWMsg_Q_TIME(R2)		; current time
	PUSHAB	ALT_Message_Desc		; Push message descriptor
	PUSHL	command_chan			; Output channel
	CALLS	#8,Print			; output a message
	BRW	1000$				; Leave

300$:	; Print FDT messages
	CMPB	DWMsg_B_Class(R2),#DW_C_FDT	; Is it an FDT message?
	BNEQ	400$				; br if not

	MOVZWL	DWMsg_L_IOST1(R2),-(SP)		; Push QIO status
	PUSHL	DWMsg_L_P6(R2)			; Push P6 arg
	PUSHL	DWMsg_L_P5(R2)			; Push P5 arg
	PUSHL	DWMsg_L_P4(R2)			; Push P4 arg
	PUSHL	DWMsg_L_P3(R2)			; Push P3 arg
	PUSHL	DWMsg_L_P2(R2)			; Push P2 arg
	PUSHL	DWMsg_L_P1(R2)			; Push P1 arg
	PUSHL	DWMsg_L_Func(R2)		; Push the func for JSB
	JSB	Get_QIO_funtion_str		; Put ASCIC string in R1
	MOVL	R1,(SP)				; Put string on stack
	MOVZWL	DWMsg_L_Func(R2),-(SP)		; Push the function code
	PUSHL	DWMsg_L_UCB(R2)			; Push the UCB
	PUSHL	DWMsg_L_IRP(R2)			; Push the IRP
	PUSHL	DWMsg_L_PID(R2)			; Push the PID
	PUSHAL	DWMsg_Q_TIME(R2)		; current time
	PUSHAB	FDT_Message_Desc		; Push message descriptor
	PUSHL	command_chan			; Output channel
	CALLS	#15,Print			; output a message
	BRW	1000$				; leave

400$:	; Print CANCEL messages
	CMPB	DWMsg_B_Class(R2),#DW_C_CANCEL	; Is it a Cancel message?
	BNEQ	500$				; br if not

	PUSHL	DWMsg_L_Func(R2)		; Push the function code
	PUSHL	DWMsg_L_UCB(R2)			; Push the UCB
	PUSHL	DWMsg_L_IRP(R2)			; Push the IRP
	PUSHL	DWMsg_L_PID(R2)			; Push the PID
	PUSHAL	DWMsg_Q_TIME(R2)		; current time
	PUSHAB	Cancel_Message_Desc		; Push message descriptor
	PUSHL	command_chan			; Output channel
	CALLS	#7,Print			; output a message
	BRW	1000$				; Leave

500$:	; Print IOPOST messages
	CMPB	DWMsg_B_Class(R2),#DW_C_IOPOST	; Is it an IOPOST message?
	BNEQ	600$				; br if not

	PUSHL	DWMsg_L_IOST2(R2)		; Push IOSB 2
	PUSHL	DWMsg_L_IOST1(R2)		; Push IOSB 1
	PUSHL	DWMsg_L_IRP(R2)			; Push the IRP
	PUSHL	DWMsg_L_PID(R2)			; Push the PID
;	PUSHL	DWMsg_L_UCB(R2)			; Push the UCB
	PUSHAL	DWMsg_Q_TIME(R2)		; current time
	PUSHAB	IOPOST_Message_Desc		; Push message descriptor
	PUSHL	command_chan			; Output channel
	CALLS	#6,Print			; output a message
	BRW	1000$				; Leave

600$:	; Print NULL messages
	PUSHL	R2				; Push the message block
	CLRL	-(SP)				; current time
	PUSHAB	NULL_Message_Desc		; Push message descriptor
	PUSHL	command_chan			; Output channel
	CALLS	#4,Print			; output a message
	BRW	1000$				; Leave

1000$:	RET


;++
;	Get_QIO_funtion_str
;
; Input:
;	4(SP) - Function code
;
; Output:
;	R0 - Destroyed (low 6-bits of function code)
;	R1 - Address of counted string representing the function name
;--
Get_QIO_funtion_str:
	EXTZV	#0,#6,4(SP),R0			; get the func code sans mods
	MOVL	Func_Tab[R0],R1
	RSB



;++
;	Get_Kernel_PTE - Find the SVAPTE for a give S0 virtual address
;
; Input:
;	R2 - a system virtual address
;
; Output:
;	R0 - Status
;	R2 - Preserved
;	R3 - SVAPTE
;	R4,R5,R6 - destroyed
;--

Get_Kernel_PTE:
	MOVL	MMG$AR_SYSPCB,R4		; System process control block
	MOVL	MMG$GL_SYSPHD,R5		; System process header
	MFPR	#PR$_IPL,R6			
	JSB	MMG$PTEREF
	BLBC	R0,10$
	UNLOCK	LOCKNAME=MMG,-			; Unlock the MMG database
		NEWIPL=R6,-
		PRESERVE=YES
10$:	RSB


;++
;	Patch_IOPOST - Add interception code to IOC$IOPOST routine
;
;
;--

Patch_IOPOST:
	PUSHR	#^M<R2,R3,R4,R5,R6>
	MOVL	G^EXE$GL_SCB,R0			; Get the SCB
	MOVL	IOC_Vector(R0),	-		; Store the prev. vector
		d_RealIOPOST(R7)
	MOVL	d_RealIOPOST(R7),R2		; Get IPL 4 interrupt vector
	BICL	#3,R2				; longword align
	ADDL	#IOC_BYPASS,R2			; Save IOC bypass entry point
	MOVL	R2,d_RealIOC_Bypass(R7)
	CMPL	(R2),#^x510CA5D0		; Are we first?
	BNEQ	100$				; leave if not

	; Let's get the kernel page's PTE (Non-paged, so no locking needed)
	JSB	Get_Kernel_PTE			; Get SVAPTE
	BLBC	R0,100$				; br on failure

	; Relax kernel page protection
	EXTV	#PTE$V_PROT,#PTE$S_PROT,-	; Get old prot from PTE
		(R3),-(SP)
	INSV	#PRT$C_UW,#PTE$V_PROT,-		; Set prot in PTE
		#PTE$S_PROT,(R3)

	; Ivalidate local TB since INST1-4 execute before global invalidation
	MTPR	R2,#PR$_TBIS

	; Invalidate translation buffer and patch IOC$IOPOST routine
	INVALIDATE_TB ADDR=R2 ENVIRON=VMS -
	    INST1=<JSB	B^IOPost_swap>

	; Restore kernel page protection
	INSV	(SP)+,#PTE$V_PROT,-		; Set prot in PTE
		#PTE$S_PROT,(R3)
	MTPR	R2,#PR$_TBIS			; Invalidate translation buffer

100$:	POPR	#^M<R2,R3,R4,R5,R6>
	RSB

;
; Perform the patch without interference from other processors
;
IOPost_swap:
	MOVL		d_RealIOC_Bypass(R7),R0
	MOVQ		(R0),d_IOC_Patch(R7)	; Save old code
	MOVW		#^X9f16,(R0)+		; JSB and abs. mode-
	MOVAB		d_IOPOST(R7),(R0)	; JSB to our IOPOST()
	RSB

Restore_IOPOST:
	PUSHR	#^M<R2,R3,R4,R5,R6>

	MOVL	d_RealIOC_Bypass(R7),R2
	CMPW	(R2),#^x9f16			; Still patched?
	BNEQ	100$				; leave if not

	; Let's get the kernel page's PTE (Non-paged, so no locking needed)
	JSB	Get_Kernel_PTE			; Get SVAPTE
	BLBC	R0,100$				; br on failure

	; Relax kernel page protection
	EXTV	#PTE$V_PROT,#PTE$S_PROT,-	; Get old prot from PTE
		(R3),-(SP)
	INSV	#PRT$C_UW,#PTE$V_PROT,-		; Set prot in PTE
		#PTE$S_PROT,(R3)

	; Ivalidate local TB since INST1-4 execute before global invalidation
	MTPR	R2,#PR$_TBIS			; Invalidate translation buffer

	; Invalidate the translation buffer and restore old instructions
	INVALIDATE_TB ADDR=R2 ENVIRON=VMS -
	    INST1=<MOVL		d_RealIOC_Bypass(R7),R0> -
	    INST2=<MOVQ		d_IOC_Patch(R7),(R0)>	; Restore old code

	; Restore kernel page protection
	INSV	(SP)+,#PTE$V_PROT,-		; Set prot in PTE
		#PTE$S_PROT,(R3)
	MTPR	R2,#PR$_TBIS			; Invalidate translation buffer

100$:	POPR	#^M<R2,R3,R4,R5,R6>
	RSB



;++
;	DevWatch_Krnl - Monitor device activity
;
; Input:	P1 - Address of device name descriptor
;		P2 - FDT flag
;		P3 - ALTSTART flag
;		P4 - STARTIO flag
;		P5 - CANCEL flag
;		P6 - IOPOST flag
;		P7 - Scratch value
;
; Output:	R0 - Status
;--
.entry DevWatch_Krnl,^m<R2,R3,R4,R5,R6,R7,R8>
	; scam on a device.  P1 = dev name descriptor; P2 to retval ptr.

	;+++++++++++++++++++++++++++++++++++++++++++++++++++
	;
	;	Initialization code
	;
	;---------------------------------------------------


	; Lock the IO Database, and prevent process deletion
	MOVL    G^CTL$GL_PCB,R4			; get my PCB address
	BISL2   #PCB$M_NODELET,-		; Set Process No-Delete bit
		PCB$L_STS(R4)			;   to avoid untimely death
	JSB	G^SCH$IOLOCKW			; lock & return

	; Get UCB
	MOVL	4(AP),R1			; devnam
	JSB	G^IOC$SEARCHDEV			; Find UCB
	BLBC	R0,30$				; br if sucess
20$:	MOVL	UCB$L_DDT(R1),R6		; Get DDT
	MOVL	R2,R8				; Save DDB in R8

	; Get a replacement FDT
	MOVL	#dlen+12,R1			; Size plus 12 byte header
	JSB	G^EXE$ALONONPAGED		; Get some npagdyn
	BLBS	R0,100$				; If failed exit error

30$:	; Error handling:  loose Mutex and exit
	PUSHL	R0
	MOVL    G^CTL$GL_PCB,R4			; get my PCB address
	JSB	G^SCH$IOUNLOCK			; unlock I/O database
	ENBINT	SRC=#0				; Set IPL to 0
	POPL	R0
	BRW	1000$

100$:
	;
	; Initialize
	;

;HACK - add code to bzero the memory

	; Init the block header
	MOVL	R2,R7				; save R2
	CLRQ	d_SysQHead(R7)			; Clear forward and back links
	MOVW	R1,d_SysSize(R7)		; Set length field
	MOVB	#DYN$C_NON_PAGED,d_SysType(R7)	; Set type field

	; Init the fake FDT
	MOVL	DDT$L_FDT(R6),R1		; Get the real FDT
	MOVQ	(R1),d_LegalFunc(R7)		; copy legal functions mask
	MOVQ	8(R1),d_BufFunc(R7)		; copy buffered functions mask
	MOVL	#^xFFFFFFFF,d_FakeFuncMask(R7)	; Capture all functions
	MOVL	#^xFFFFFFFF,d_FakeFuncMask+4(R7); Capture all functions
	MOVAB	d_FDT(R7),d_FakeFuncRtn(R7)	; pointer to code

	; Init the parameter section before the code
	CLRQ	d_PendQ(R7)			; Init queue of pending reqs.
	CLRQ	d_FreeQ(R7)			; Init queue of free reqs.
	CLRL	d_FreeSize(R7)			; Clear count of alloced msgs
	MOVL	#^A/****/,d_Text(R7)		; Store a pattern to make
	MOVL	#^A/****/,d_Text+4(R7)		;   it easy to find this
	MOVL	#^A/****/,d_Text+8(R7)		;   block by searching in
	MOVL	#^A/****/,d_Text+12(R7)		;   SDA.

	CLRL	d_Count_FDT(R7)			; Clear operation count
	CLRL	d_Count_Alt(R7)			; Clear operation count
	CLRL	d_Count_Start(R7)		; Clear operation count
	CLRL	d_Count_Cancel(R7)		; Clear operation count
	CLRL	d_Count_IOPost(R7)		; Clear operation count

	CLRL	d_Target_PID(R7)		; Clear target PID
	MOVL	R8,d_Target_DDB(R7)		; Store the DDB
	CLRL	d_Flags(R7)			; Clear flags
	CLRL	d_Scratch(R7)			; Clear scratch
	CLRL	d_Debug1(R7)			; Clear debuging info
	MOVC3	#dcode_len,dCode,d_Code(R7)		; copy our code into S0

	; Replace old FDT
	CLRL	d_RealFDT(R7)			; Off by default
	BLBC	8(AP),120$			; Monitor FDT?
	INVALIDATE_TB ADDR=R6 ENVIRON=VMS -
	    INST1=<MOVL	  DDT$L_FDT(R6),d_RealFDT(R7)>-	; Store the real FDT
	    INST2=<MOVAL  12(R7),DDT$L_FDT(R6)>		; Patch in the new FDT

120$:	; Replace old ALTSTART
	CLRL	d_RealAltStart(R7)		; Off by default
	BLBC	12(AP),125$			; Monitor AltStart vector?
	INVALIDATE_TB ADDR=R6 ENVIRON=VMS -
	    INST1=<MOVL  DDT$L_ALTSTART(R6),d_RealAltStart(R7)>-
	    INST2=<MOVAL d_AltStart(R7),DDT$L_ALTSTART(R6)>

125$:	; Replace old STARTIO
	CLRL	d_RealStartIO(R7)		; Off by default
	BLBC	16(AP),130$			; Monitor StartIO vector?
	INVALIDATE_TB ADDR=R6 ENVIRON=VMS -
	    INST1=<MOVL	 DDT$L_START(R6),d_RealStartIO(R7)>-
	    INST2=<MOVAL d_StartIO(R7),DDT$L_START(R6)>

130$:	; Replace old CANCEL
	CLRL	d_RealCancel(R7)		; Off by default
	BLBC	20(AP),140$			; Monitor Cancel requests?
	INVALIDATE_TB ADDR=R6 ENVIRON=VMS -
	    INST1=<MOVL	 DDT$L_CANCEL(R6),d_RealCancel(R7)>-
	    INST2=<MOVAL d_Cancel(R7),DDT$L_CANCEL(R6)>

140$:	; Replace old IOC$IOPOST in the SCB (System Control Block)
	CLRL	d_RealIOPOST(R7)		; Off by default
	BLBC	24(AP),150$			; Monitor IOPOST interrupts?
	JSB	Patch_IOPOST			; Plug that sucker

150$:	MOVL    G^CTL$GL_PCB,R4			; get my PCB address
	JSB	G^SCH$IOUNLOCK			; unlock I/O database
	ENBINT	SRC=#0				; Set IPL to 0

	; Find IPID
	MOVL	PCB$L_PID(R4),R1		; Get server EPID
	MOVL	R1,my_pid			; Keep copy of PID
	JSB	G^EXE$EPID_TO_IPID		; convert to internal PID
;	BNEQ	180$
;	BRW	900$
180$:	MOVL	R1,d_IPID(R7)		; Store internal PID
	CLRL	done_flag

	; Assign a channel to sys$command
190$:	$ASSIGN_S	devnam=Command_Desc,chan=command_chan,-
			acmode=#PSL$C_USER
	BLBS	R0,195$				; If sucess, continue
	BRW	900$

195$:	$QIO_S	chan=command_chan,func=#IO$_READVBLK,astadr=TT_Input,-
		P1=#buffer,P2=#bsize
	BLBS	R0,200$				; If sucess, continue
	BRW	900$

;HACK!!! compare input device DDB with Target DDB and exit if equal

	;+++++++++++++++++++++++++++++++++++++++++++++++++++
	;
	;	Top of main processing loop
	;
	;---------------------------------------------------


	; Sleep and wait for input
200$:	CALLS	#0,G^SYS$HIBER			; Zzzzzzzzz.........

210$:	; Ok, ok, we're awake.  Go to work

	TSTL	done_flag			; Check exit flag
	BNEQ	400$				; exit if non-zero
	TSTL	d_Flags(R7)			; Check exit flag
	BNEQ	400$				; loop if zero

	REMQHI	d_PendQ(R7),R2			; Get next message
	BVS	200$				; br if no more
	ADAWI	#-1,d_Scratch(R7)		; update # outstanding

	PUSHL	R2				; Pass message block
	PUSHL	command_chan			; Pass channel
	CALLS	#2,Print_Message

	INSQTI	(R2),d_FreeQ(R7)		; Put message in free queue
	BRB	210$				; Loop

	;+++++++++++++++++++++++++++++++++++++++++
	;
	;	Clean-up code
	;
	;-----------------------------------------

400$:	; Deassign the channel
	$DASSGN_S	chan=command_chan

;HACK!!! When restoring vectors, we should check that no one modified them
;	after we did.

900$:	; Restore the old FDT
	MOVL    G^CTL$GL_PCB,R4			; get my PCB address
	JSB	G^SCH$IOLOCKW			; lock & return

	TSTL	d_RealFDT(R7)			; Check for real FDT
	BEQL	902$				; br if none
	INVALIDATE_TB ADDR=R6 ENVIRON=VMS -
	    INST1=<MOVL	d_RealFDT(R7),DDT$L_FDT(R6)>

902$:	TSTL	d_RealAltStart(R7)		; Check for real AltStart
	BEQL	904$				; br if none
	INVALIDATE_TB ADDR=R6 ENVIRON=VMS -
	    INST1=<MOVL	d_RealAltStart(R7),DDT$L_ALTSTART(R6)>

904$:	TSTL	d_RealStartIO(R7)		; Check for real StartIO
	BEQL	906$				; br if none
	INVALIDATE_TB ADDR=R6 ENVIRON=VMS -
	    INST1=<MOVL	d_RealStartIO(R7),DDT$L_START(R6)>

906$:	TSTL	d_RealCancel(R7)		; Check for real Cancel rtn.
	BEQL	907$				; br if none
	INVALIDATE_TB ADDR=R6 ENVIRON=VMS -
	    INST1=<MOVL	d_RealCancel(R7),DDT$L_CANCEL(R6)>

907$:	TSTL	d_RealIOPOST(R7)		; Check for real IOPOST vector
	BEQL	908$				; br if none
	JSB	Restore_IOPOST

908$:	JSB	G^SCH$IOUNLOCK			; unlock I/O database
	ENBINT	SRC=#0				; Set IPL to 0

	; Free the message blocks!
910$:	REMQHI	d_FreeQ(R7),R0			; Get next message
	BVS	920$				; br if no more
	JSB	G^EXE$DEANONPAGED		; Deallocate
	DECL	d_FreeSize(R7)			; One less message
	BRB	910$				; Loop
920$:	REMQHI	d_PendQ(R7),R0			; Get next message
	BVS	930$				; br if no more
	JSB	G^EXE$DEANONPAGED		; Deallocate
	DECL	d_FreeSize(R7)			; One less message
	BRB	920$				; Loop
930$:
	MOVL	d_FreeSize(R7),@28(AP)		; return # lost MsgBlks
	BICL2   #PCB$M_NODELET,-		; Set Process No-Delete bit
		PCB$L_STS(R4)			;   to avoid untimely death
	MOVL	R7,R0				; Get S0 block
	JSB	G^EXE$DEANONPAGED		; Deallocate
	MOVL	#SS$_NORMAL,R0			; success!

1000$:	RET

Last_Page::	; End of locked page range

;++
;	DevWatch - CMKRNL dispatcher for DevWatch_Krnl
;
;--

.entry DevWatch,^m<>
	$LKWSET_S	Locked_Pages		; Bolt those suckers down
	MOVL	AP,-(SP)
	MOVAB	DevWatch_Krnl,-(SP)
	CALLS	#2,G^SYS$CMKRNL
	RET

.end
