; **********************************************************************
; initdira - Provides BIOS (XIOS) assembler interface for INITDIR.PLI
; 	   - Also provides examples of how to:
;		1. Call CCP/M-86 XIOS
;		2. Lock up disk system for direct disk I/O
;		3. Lock up console, to prevent a job from being switched out
; **********************************************************************


	cseg

	public	bstdma		; sets DMA segment and offset
	public	rdsec		; reads a physical sector
	public	sectrn		; translates a sector
	public	seldsk		; selects a drive
	public	setsec		; sets the sector to be read/written
	public	settrk		; sets the track to be read/written
	public	wrsec		; writes a physical sector

	public	openvec		; returns open files vector
	public	syslock		; locks up the disk system
	public	sysunlock	; unlocks the disk system
	public	conlock		; locks the console into foreground mode
	public	conunlock	; unclocks the console


IO_SELDSK equ	  9		; XIOS function number
IO_READ   equ	 10		; XIOS function number
IO_WRITE  equ	 11		; XIOS function number
P_PRIORITY equ	145		; BDOS function: set Process PRIORITY
S_SYSDAT  equ	154		; BDOS function: get SYStem DATa page address
P_PDADR	  equ	156		; BDOS function: get Process Descriptor address
Q_OPEN	  equ	135		; Open Queue
Q_READC	  equ	138		; Read Queue Conditional
Q_WRITEC  equ	139		; Write Queue Conditional

XIOS_ptr  equ	dword ptr .28h	; loc of XIOS entry in SYSDAT 
OPVEC	  equ	word ptr .88h	; loc of Open_Files_on_Drives vector
UDA_seg	  equ	word ptr 10h	; loc of UDA seg in Process Descriptor



;*****************************************************************
;***   PL/I Utility Functions                                  ***
;*****************************************************************
getp1:	; get single byte parameter to register DL
	mov	bx,[bx]		;BX = .char
	mov	dl,[bx]		;to register DL
	ret

getp2:	; get single word value to DX
getp2i:	; same as getp2
	mov	bx,[bx]
	mov	dx,[bx]
	ret


getsu:	;get sysdat and uda addrs
	;enters: DS=local data seg
	;exits:  DS=SYSDAT seg, ES=UDA seg (for call to XIOS)
	mov	cx,udaaddr	;get the saved value
	or	cx,cx		;set flags
	jz	getsu1		;uninitialized, go do the OS call
	mov	es,cx		;we've been here before, just load regs
	mov	cx,sysaddr
	mov	ds,cx
	ret

getsu1:
	mov	cl,P_PDADR	;get Process Descriptor
	int	0E0h		;call BDOS
	mov	cx,es:UDA_seg[bx] ;grab UDA_seg
	mov	udaaddr,cx	;save for future calls
	push	cx		;save UDA_seg
	  mov	cl,S_SYSDAT	;get address of SYStem DATa area
	  int	0E0h		;call BDOS
	  mov	cx,es		;mov ds,es
	  mov	sysaddr,cx	;save for future calls
	  mov	ds,cx
	pop	es		;restore UDA_seg
	ret



;*****************************************************************
;***   Simulate old XIOS style functions                       ***
;*****************************************************************
bstdma:	; sets DMA segment and offset
	call	getp2		;dma address to DX
	mov	dmaoff,dx	;stuff addr in IOPB's offset field
	mov	dmaseg,ds	;assume all addresses relative to DS
	ret			;no BDOS/XIOS call, just init the IOPB


sectrn:	; translates a sector
	call	getp2		;get sector number to DX
	mov	bx,dx		;no translation: return (unchanged) value
	ret


setsec:	; sets the sector to be read/written
	call	getp2i		;sector number to DX
	mov	sector,dx	;stuff sector into IOPB
	ret


settrk:	; sets the track to be read/written
	call	getp2i		;track number to DX
	mov	track,dx	;stuff track into IOPB
	ret


;*****************************************************************
;***  Physical I/O calls                                       ***
;*****************************************************************
rdsec:	; reads a physical sector
	mov	ax,IO_READ
	jmp	xiosiopb	;jump around this code

wrsec:	; writes a physical sector
	mov	ax,IO_WRITE	;fall thru to xiosiopb

xiosiopb:	;put the IOPB on the stack, call XIOS
	push	ds		;ds will contain SYSDAT seg
	push	es		;es will contain UDA seg
		;someday change this to a block move?
	mov	ch,mscnt
	mov	cl,drv
	push	cx		;1st word of IOPB
	mov	cx,track
	push	cx		;2nd word
	mov	cx,sector
	push	cx		;3rd word
	mov	cx,dmaseg
	push	cx		;4th word
	mov	cx,dmaoff
	push	cx		;5th word

	push	ax		;save XIOS_function
	call	getsu		;set up DS-SYSDAT and ES-UDA
	pop	ax		;restore XIOS_function
	callf	XIOS_ptr	;call indirect the XIOS
				;bl contains return status
	pop	cx		;dma offset
	pop	cx		;dma segment
	pop	cx		;track
	pop	cx		;sector
	pop	cx		;drv & mscnt
	pop	es		;restore original es
	pop	ds		;ditto for ds
	ret	


;*****************************************************************
;*** XIOS Select Disk Routine                                  ***
;*****************************************************************
seldsk:	; selects a drive
	; also resets login sequence number of drive to 0,
	;   to force permanent media to be logged in again on disk reset

	call	getp1		;drive to DL
	mov	drv,dl		;stuff drive into IOPB
	push es ! push ds	;save context ************
				;do the XIOS SELDSK call
	push	dx		;save drive
	call	getsu		;set up DS and ES
	pop	cx		;restore drive
	mov	ax,IO_SELDSK
	mov	dx,0		;this better not be the first call
	callf	XIOS_ptr	;call indirect XIOS
				;xfer DPH locally
	pop es ! push es	;restore & save Data Segment into es
	mov	di,offset dph	;set up destination
	mov	si,bx		;ptr to dph returned from XIOS call
	mov	log_seqn[si],0	;force disk reset: 0 login sequence number
	mov	cx,dphsiz
	rep movsb		;move copy of DPH into local storage
				;xfer DPB locally
	mov	di,offset dpb	;set up destination
	mov	si,es:dpbptr	;get this info from DPH
	mov	cx,dpbsiz
	rep movsb		;move copy of DPB into local storage
				;cleanup
	pop ds ! pop es		;restore context ************
	mov	dpbptr,offset dpb ;set up local ptr in DPH
	mov	bx,offset dph	;return address of local copy of DPH
	ret


;*****************************************************************
;***  Open Drives Vector                                       ***
;*****************************************************************
openvec:	; returns vector of drives with open files
	push	es		;save extra seg
	push	ds		;save data seg
	mov	cl,S_SYSDAT	;look in the system data page
	int	0E0h		;call bdos
	mov	ax,es:OPVEC	;get the vector of drives containing open files
	mov	bx,ax		;stuff both regs
	pop	ds		;restore data seg
	pop	es		;restore extra seg
	ret


;*****************************************************************
;***  System Lock and Unlock Routines                          ***
;*****************************************************************
syslock:	; locks up the disk system
		; returns 0 in ax,bx if everything ok, -1 o.w.
	push	es		;save extra seg
	push	ds		;save data seg

	mov	cl,S_SYSDAT	;look in the system data page
	int	0E0h		;call bdos
	mov	cx,es:OPVEC	;get the vector of drives containing open files
	test	cx,0FFFFh	;check all drives
	jnz	syslfail	;fail if any open files

	mov	cx,Q_OPEN
	mov	dx,offset qpb	;mxdisk queue parm block
	int	0E0h		;call bdos
	or	ax,ax		;test return
	jnz	sysltry2	;if non zero, try kluge
	mov	cx,Q_READC	;see if we can read the queue
	mov	dx,offset qpb	;insurance
	int	0E0h		;call bdos
	or	ax,ax		;test retrun
	jnz	syslfail	;fail if we can't read mxdisk queue
	jmp	syslokay	;okay, tell 'em so

sysltry2:			;kluge for old systems
	mov	cx,Q_OPEN
	mov	dx,offset qpb2	;mxdisk queue parm block
	int	0E0h		;call bdos
	or	ax,ax		;test return
	jnz	syslfail	;if non zero
	mov	cx,Q_READC	;see if we can read the queue
	mov	dx,offset qpb2	;insurance
	int	0E0h		;call bdos
	or	ax,ax		;test retrun
	jnz	syslfail	;fail if we can't read mxdisk queue
syslokay:
	mov ax,0 ! jmp syslret	;return code 0, everything okay
syslfail:
	mov ax,0FFFFh		;return code -1, failure
syslret:
	mov	bx,ax		;stuff both regs
	pop	ds		;restore data seg
	pop	es		;restore extra seg
	ret



sysunlock:	;undoes a 'syslock' function
	mov	cx,Q_WRITEC	;conditionally write to mxdisk queue
	mov	dx,offset qpb
	int	0E0h
	mov	cx,Q_WRITEC	;kluge to handle old systems
	mov	dx,offset qpb2
	int	0E0h
	ret


;*****************************************************************
;***  Console Lock and Unlock Routines                         ***
;*****************************************************************

CCB_BACKGRD  equ 0002h		;Console in Background mode
CCB_NOSWITCH equ 0008h		;Console not allowed to switch mode
CCB_SIZE     equ 2Ch		;size of CCB 
CCB_STATE    equ word ptr 0Eh	;addr of Console State word in CCB
CON_NUM      equ byte ptr 020h	;addr of console number in PD
CCB_PTR      equ word ptr .054h	;addr of CCB table in SYSDAT

PD_FLAG	     equ word ptr 06	;addr of FLAGs word in PD
PD_KEEP	     equ 0002h		;Keep Process Flag
TOP_PRIOR    equ 151		;What we set to for Top Priority (arbitrary)
REG_PRIOR    equ 200		;Default Regular Priority


conlock:	;locks the console into the foreground, sets Keep Flag,
		;  and boosts priority
		;returns 0 in ax,dx if everything okay, -1 o.w.
	push	es		;save extra seg
	call	concalc		;ES=SYSDAT, bx=CCB offset
	test	es:CCB_STATE[bx],CCB_BACKGRD	;is console in background?
	jnz	conlfail	;background operation not allowed!
	or	es:CCB_STATE[bx],CCB_NOSWITCH	;make sure they don't switch
	mov	cl,P_PDADR	;get Process Descriptor
	int	0E0h		;call BDOS
	or	es:PD_FLAG[bx],PD_KEEP ;set Keep flag
	mov	dl,TOP_PRIOR	;let's be quick
	mov	cl,P_PRIORITY	;set Priority
	int	0E0h		;call BDOS

conlokay:
	mov	ax,0 ! jmp conlret
conlfail:
	mov	ax,0FFFFh
conlret:
	mov	bx,ax
	pop	es		;restore extra seg
	ret



conunlock:	;undoes the 'conlock' function
	push	es		;save extra seg
	call	concalc		;ES=sysdat, bx=CCB offset
	and	es:CCB_STATE[bx],NOT CCB_NOSWITCH ;let them switch now
	mov	cl,P_PDADR	;get Process Descriptor
	int	0E0h		;call BDOS
	and	es:PD_FLAG[bx],NOT PD_KEEP ;reset Keep flag
	mov	dl,REG_PRIOR	;finished with quick
	mov	cl,P_PRIORITY	;set Priority
	int	0E0h		;call BDOS

	pop	es		;restore extra seg
	ret



concalc:	;put SYSDAT in ES, offset of CCB in bx
	mov	cx,P_PDADR	;get Process Descriptor Addr
	int	0E0h		;call the bdos
	xor	ax,ax		;clear ax
	mov	al,es:CON_NUM[bx] ;grab the console number
	mov	cx,CCB_SIZE	;stuff cx with size
	mul	cx		;compute ccb address
	push	ax		;save ccb offset
	mov	cl,S_SYSDAT	;get the System Data Segment
	int	0E0h		;call the bdos
	pop	bx		;restore ccb offset
	add	bx,es:CCB_PTR	;compute offset of ccb
	ret



;*****************************************************************
;***  Data Segment                                             ***
;*****************************************************************

	dseg	

sysaddr	dw	0	; save location for sysdat addr
udaaddr	dw	0	; save location for process uda addr

dphsiz	equ	014h	; size of Disk Parm Header
dph	rb	dphsiz	; local save area
log_seqn equ	byte ptr 6	; byte to force reset of permanent media
dpbptr	equ	word ptr dph+8	; word of interest: DPB offset

dpbsiz	equ	011h	; size of Disk Parameter Buffer
dpb	rb	dpbsiz	; local save area


iopb	rb	0	; the iopb structure filled in by above rtns
mscnt	db	1	; multi sector count
drv	rb	1	; select drive
track	rw	1	; select track
sector	rw	1	; select sector
dmaseg	rw	1	; set dma address
dmaoff	rw	1	; set dma address
iopbsiz	equ	(offset $)-(offset iopb)


qpb	rb	0	; queue parameter block
	dw	0	; reserved
	dw	0	; queueid
	dw	0	; nmsgs
	dw	0	; buffer
	db	'MXdisk  ' ; queue name

qpb2	rb	0	; queue parameter block number 2: be persistent
	dw	0	; reserved
	dw	0	; queueid
	dw	0	; nmsgs
	dw	0	; buffer
	db	'mxdisk  ' ; queue name
