TITLE  DRIVEIO
;
; --- CORVUS/IBM DRIVE INTERFACE UNIT FOR MICROSOFT ---
;	      PASCAL AND BASIC COMPILERS
;	  CONST ][ VERSION FOR DOS 1.10 & 2.0
;
;		VERSION 1.41  BY  BRK
;	   (MICROSOFT ASSEMBLER VERSION )
;
;
;   NOTE: THIS INTERFACE UNIT NOW SUPPORTS BOTH PASCAL AND BASIC
;	  COMPILERS BUT IT MUST BE RE-ASSEMBLED WITH THE APPROPRIATE
;	  SETTING OF THE  "LTYPE"  EQUATE TO DO THIS FOR EACH LANGUAGE.
;
;
;
;	THIS UNIT IMPLEMENTS  9  PROCEDURES:
;
;	INITIO
;	BIOPTR		- CONST. ][
;	SETSRVR		- CONST. ][
;	FINDSRVR	- CONST. ][
;	NETCMD		- CONST. ][
;	CDRECV = DRVRECV
;	CDSEND = DRVSEND
;
;	THE CALLING PROCEDURE IN PASCAL IS :
;
;		CDSEND (VAR st : longstring )
;
;	THE FIRST TWO BYTES OF THE STRING ARE THE LENGTH
;	OF THE STRING TO BE SENT OR THE LENGTH OF THE 
;	STRING RECEIVED.
;
;		function INITIO	: INTEGER
;
;	THE FUNCTION RETURNS A VALUE TO INDICATE THE STATUS OF
;	THE INITIALIZATION OPERATION.  A VALUE OF ZERO INDICATES
;	THAT THE INITIALIZATION WAS SUCCESSFUL.  A NON-ZERO VALUE
;	INDICATES THE I/O WAS NOT SETUP AND THE CALLING PROGRAM
;	SHOULD NOT ATTEMPT TO USE THE CORVUS DRIVERS.
;
;		function BIOPTR	: INTEGER
;
;	THE FUNCTION RETURNS A 16 BIT POINTER TO THE "CORTAB"
;	BIOS TABLE IN THE CORVUS "BIOS" DRIVERS.  THIS ROUTINE
;	SHOULD NOT BE EXECUTED BEFORE A SUCCESSFUL USE OF THE
;	"INITIO" ROUTINE (ABOVE).  NOTE:  THE RETURNED VALUE IS
;	RELATIVE TO "SEGMENT" ZERO, AND A RETURNED VALUE OF ZERO
;	INDICATES THAT THE "CORTAB" TABLE COULD NOT BE FOUND.
;
;		function SETSRVR ( srvr : integer): INTEGER
;
;	THE FUNCTION RETURNS THE "BOOT SERVER" NETWORK ADDRESS.
;	IF THE INPUT PARAMETER IS LESS THAN  255 ( BUT NOT NEGATIVE ),
;	IT WILL BE TAKEN AS A RESET OF THE DEFAULT SERVER # WHEN
;	USING THE  SEND & RECIEVE  ROUTINES.  IF IT IS GREATER THAN 255
;	OR NEGATIVE, NO CHANGE OF THE DEFAULT SERVER # WILL BE MADE.
;	NOTE: THE DEFAULT SERVER # IS AUTOMATICALLY SET TO THE
;	BOOT SERVER # WHEN THE  "INITIO" FUNCTION IS EXECUTED.
;	
;		function FINDSRVR : INTEGER
;
;	THE FUNCTION RETURNS THE NETWORK ADDRESS OF A VALID DISK SERVER.
;	IF THE RETURNED VALUE IS GREATER THAN 63 OR NEGATIVE, THE COMMAND
;	FAILED TO FIND A SERVER ( THE FLAT CABLE CARDS WOULD DO THIS ).
;
;		function CARDID : INTEGER
;
;	THE FUNCTION RETURNS THE CORVUS INTERFACE CARD TYPE ( 0 - OMNINET,
;	1 - FLAT CABLE ).
;
;		function NETCMD ( VAR inp, VAR out: longstring) : INTEGER
;
;	THE FUNCTION IS USED TO SEND/RECIEVE DATA TO A NETWORK SERVER.
;	STRING  inp  SPECIFIES THE COMMAND TO SEND TO THE SERVER,
;	AND STRING  out  IS WHERE ANY RETURNED DATA WILL BE PLACED
;	( THE STRING LENGTH OF  out  WILL NOT BE CHANGED BY THIS
;	OPERATION UNLESS THE COMMAND FAILED- IN WHICH CASE THE LENGTH
;	WILL BE SET TO ZERO).  THE VALUE OF THE FUNCTION WILL BE
;	RETURNED AS  ZERO  IF THE OPERATION WAS SUCCESSFUL, AND
;	NON-ZERO IF IT FAILED.
;	NOTE: THE SERVER # USED WILL BE THE "BOOT SERVER" # UNLESS
;	THE DEFAULT IS CHANGED BY THE  "SETSRVR" CMD.
;
;
;
;
;	THE CALLING PROCEDURE BASIC IS :
;
;		CALL CDSEND (B$ )
;
;	THE FIRST TWO BYTES OF THE STRING ARE THE LENGTH
;	OF THE STRING TO BE SENT OR THE LENGTH OF THE 
;	STRING RECEIVED ( I.E. LEFT$(B$,2) ).
;
;		CALL INITIO (A%)
;
;	THE FUNCTION RETURNS A VALUE TO INDICATE THE STATUS OF
;	THE INITIALIZATION OPERATION.  A VALUE OF ZERO INDICATES
;	THAT THE INITIALIZATION WAS SUCCESSFUL.  A NON-ZERO VALUE
;	INDICATES THE I/O WAS NOT SETUP AND THE CALLING PROGRAM
;	SHOULD NOT ATTEMPT TO USE THE CORVUS DRIVERS.
;
;		CALL BIOPTR (A%)
;
;	THE FUNCTION RETURNS A 16 BIT POINTER TO THE "CORTAB"
;	BIOS TABLE IN THE CORVUS "BIOS" DRIVERS.  THIS ROUTINE
;	SHOULD NOT BE EXECUTED BEFORE A SUCCESSFUL USE OF THE
;	"INITIO" ROUTINE (ABOVE).  NOTE:  THE RETURNED VALUE IS
;	RELATIVE TO "SEGMENT" ZERO, AND A RETURNED VALUE OF ZERO
;	INDICATES THAT THE "CORTAB" TABLE COULD NOT BE FOUND.
;
;		CALL SETSRVR (A%)     here  A% is used for input and output
;
;	THE FUNCTION RETURNS THE "BOOT SERVER" NETWORK ADDRESS.
;	IF THE INPUT PARAMETER IS LESS THAN  255 ( BUT NOT NEGATIVE ),
;	IT WILL BE TAKEN AS A RESET OF THE DEFAULT SERVER # WHEN
;	USING THE  SEND & RECIEVE  ROUTINES.  IF IT IS GREATER THAN 255
;	OR NEGATIVE, NO CHANGE OF THE DEFAULT SERVER # WILL BE MADE.
;	NOTE: THE DEFAULT SERVER # IS AUTOMATICALLY SET TO THE
;	BOOT SERVER # WHEN THE  "INITIO" FUNCTION IS EXECUTED.
;	
;		CALL FINDSRVR (A%)
;
;	THE FUNCTION RETURNS THE NETWORK ADDRESS OF A VALID DISK SERVER.
;	IF THE RETURNED VALUE IS GREATER THAN 63 OR NEGATIVE, THE COMMAND
;	FAILED TO FIND A SERVER ( THE FLAT CABLE CARDS WOULD DO THIS ).
;
;		CALL CARDID (A%)
;
;	THE FUNCTION RETURNS THE CORVUS INTERFACE CARD TYPE ( 0 - OMNINET,
;	1 - FLAT CABLE ).
;
;		CALL NETCMD ( A$,B$,C%)
;
;	THE FUNCTION IS USED TO SEND/RECIEVE DATA TO A NETWORK SERVER.
;	STRING  A$  SPECIFIES THE COMMAND TO SEND TO THE SERVER,
;	AND STRING  B$  IS WHERE ANY RETURNED DATA WILL BE PLACED
;	( THE STRING LENGTH OF  out  WILL NOT BE CHANGED BY THIS
;	OPERATION UNLESS THE COMMAND FAILED- IN WHICH CASE THE LENGTH
;	WILL BE SET TO ZERO).  THE VALUE OF THE FUNCTION WILL BE
;	RETURNED ( IN C% ) AS  ZERO  IF THE OPERATION WAS SUCCESSFUL, AND
;	NON-ZERO IF IT FAILED.
;	NOTE: THE SERVER # USED WILL BE THE "BOOT SERVER" # UNLESS
;	THE DEFAULT IS CHANGED BY THE  "SETSRVR" CMD.
;
;=============================================================
;			REVISION HISTORY
;
; FIRST VERSION : 10-05-82  BY BRK
; 		: 11-01-82  improved turn around delay for mirror
;		: 02-16-83  CONST. ][ version
;		: 05-16-83  added support for Basic
;		: 07-06-83  fixed bug in FINDSRVR routine
; V1.40		: 07-29-83  updated for DOS 2.0 
; V1.41		: 08-04-83  set timeout to zero to avoid ROM bug
;
;=============================================================
;
TRUE	EQU	0FFFFH
FALSE	EQU	0
;
PASCAL	EQU	1	; LANGUAGE TYPE DESCRIPTOR
BASIC	EQU	2	; LANGUAGE TYPE DESCRIPTOR
;
LTYPE	EQU	PASCAL	; SET TO LANGUAGE TYPE TO BE USED WITH
INTDVR	EQU	FALSE	; SET TO FALSE TO DISABLE INTERNAL FLAT CABLE DRIVER
;
;
; ----- CORVUS EQUATES -----
;
DATA	EQU	2EEH	; DISC I/O PORT #
STAT	EQU	2EFH	; DISC STATUS PORT
DRDY	EQU	1	; MASK FOR DRIVE READY BIT
DIFAC	EQU	2	; MASK FOR BUS DIRECTION BIT
ROMSEG	EQU	0DF00H	; LOCATION OF CORVUS ROM
BIOSSEG	EQU	60H	; STD IBM BIOS SEGMENT ADDRESS
ABTCTR	EQU	0A00H	; VALUE TO SET TIMEOUT AND # OF RETRYS
;			;   v1.41  timeouts=0
;
FCALL	EQU	9AH	; OPCODE FOR FAR CALL
FJMP	EQU	0EAH	; OPCODE FOR FAR JUMP
;
; --- MSDOS EQUATES ( V2.0 ) ---
;
VERCMD	EQU	30H	; BDOS COMMAND TO GET VERSION #
HOPEN	EQU	3DH	; BDOS COMMAND TO "OPEN" A FILE HANDLE
HCLOSE	EQU	3EH	; BDOS COMMAND TO "CLOSE" A FILE HANDLE
HREAD	EQU	3FH	; BDOS COMMAND TO "READ" FROM A FILE
HWRITE	EQU	40H	; BDOS COMMAND TO "WRITE" TO A FILE
;
PGSEG	SEGMENT 'CODE'
	ASSUME	CS:PGSEG
;
;
	IF	LTYPE EQ PASCAL
	DB	'CORVUS/IBM PC CONST. ][ PASCAL DRIVER AS OF 08-04-83'
	ENDIF
;
	IF	LTYPE EQ BASIC
	DB	'CORVUS/IBM PC CONST. ][ BASIC  DRIVER AS OF 08-04-83'
	ENDIF
;
; --- COPY OF "ROM" FAR JUMP TABLE ---
;
ROMTAB	PROC	NEAR
	DB	FJMP
	DW	0,ROMSEG	; FAR JUMP TO COLD BOOT ROM ENTRY
	DB	FJMP
	DW	3,ROMSEG	; FAR JUMP TO WARM START ROM ENTRY
	DB	FJMP
	DW	6,ROMSEG	; FAR JUMP TO I/O ROM ENTRY
	DB	FJMP
	DW	9,ROMSEG	; FAR JUMP TO DUMMY "IRET" ENTRY
LENTAB	EQU	offset $-offset ROMTAB	; LENGTH OF TABLE
ROMTAB	ENDP
;
; --- COPY OF CORVUS TABLE IDENTIER ---
;
CORTAB	DB	'CORTAb'	; VERSION FOR CONST. ][
;
; --- COPY OF UTILITY "HOOK" DRIVER NAME ---
;
UTILPTR	DB	'UTILHOOK',0
;
;
; --- THESE DATA POINTERS MUST BE KEPT IN THE SAME RELATIVE ORDER
;
SNDPTR	DW	0		; BUFFER TO SAVE POINTER TO 'SEND' STRING
SNDSEG	DW	0		; BUFFER TO SAVE 'SEND' STRING SEGMENT #
;
CORVEC	DW	0,0		; BUF TO SAVE DOUBLE WORD POINTER TO "CORTAB"
;
; --- MISC DATA AND BUFFERS ----
;
CORPTR	DW	0		; BUFFER FOR "CORTAB" POINTER
;				;  INITIALIZE INITIALLY TO ZERO
CRDTYPE	DB	1		; BUFFER TO SAVE "CARD TYPE" BYTE
BOOTSRVR DB	0FFH		; BUFFER FOR "BOOT SERVER"
SRVR	DB	0FFH		; BUFFER FOR "DEFAULT SERVER"
;
;
; === INITIALIZE CORVUS I/O DRIVERS ===
;
;	THIS ROUTINE MUST BE CALLED
;	ONCE TO SETUP THE DRIVERS BEFORE
;	THEY ARE USED. IF THE ROUTINE DOES
;	ANYTHING THAT CAN ONLY BE DONE ONCE,
;	IT MUST DISABLE THIS SECTION SO THAT
;	AND ACCIDENTAL SECOND CALL WILL NOT
;	LOCK UP THE HARDWARE.
;
	PUBLIC INITIO
;
INITIO	PROC	FAR
	PUSH	DS
	PUSH	ES
	PUSH	CS
	POP	ES		; SET ES=CS
	CLD
;
	MOV	AH,VERCMD	; MSDOS VERSION CHECK COMMAND
	INT	21H		; GET VERSION # OF DOS
	OR	AL,AL		; IS IT V 1.1 OR 1.0?
	JZ	IV11		; YES, SO TRY FINDING "CORTAb"
;
	PUSH	CS
	POP	DS		; SET TO LOCAL SEGMENT FOR TESTING
;
	MOV	AH,HOPEN	; SET MSDOS 2.X, OPEN HANDLE COMMAND
	MOV	AL,2		; OPEN FOR R/W
	MOV	DX,offset UTILPTR ; POINT TO "HOOK" DRIVER NAME
	INT	21H		; DO IT
	JC	IV12		; IF ERROR, TRY FOR IBM ROM
;
	MOV	BX,AX		; GET "HANDLE" IN (BX)
	MOV	AH,HWRITE	; GET WRITE CMD
	MOV	CX,2		; SET TO WRITE 2 CHARS
	MOV	DX,offset UTILPTR ; USE NAME FOR SOURCE OF CHARACTERS
	INT	21H		; THIS SHOULD RESET "POINTER" IN DRIVER
;
	MOV	AH,HREAD	; SET READ CMD
	MOV	CX,4		; SET TO READ  DOUBLE WORD
	MOV	DX,offset CORVEC ; POINT TO DESTINATION OF READ
	INT	21H		; DO IT
;
	MOV	AH,HCLOSE	; GET CLOSE CMD
	INT	21H		; CLOSE HANDLE
;
	LDS	BX,dword ptr CORVEC ; GET POSSIBLE POINTER TO "CORTAb"
	CALL	BIOT1		; TEST FOR "CORTAb"
	JNC	OKEXIT		; IF OK, EXIT
	JMP	IV12		; OTHERWISE PROCEED
;
IV11:	MOV	AX,BIOSSEG	; SET TO TEST STD IBM SEGMENT ADD
	CALL	BIOTST		; TEST BIOS AND LINK TO IT IF OK
	JNC	OKEXIT		; IF OK, EXIT
	MOV	AX,BIOSSEG-20H	; TRY MICROSOFT STD LOCATION (40H)
	CALL	BIOTST
	JNC	OKEXIT		; IF OK, EXIT
;
IV12:	MOV	AX,ROMSEG
	MOV	DS,AX		; SET DS=ROM SEGMENT
	XOR	AX,AX		; GET A ZERO
	MOV	BX,AX		; POINT TO START OF ROM
	MOV	DI,AX		; INIT CHECKSUM COUNTER
	MOV	CX,4		; CHECK FOR  4  JUMPS AT START OF ROM
;
CKROM:	MOV	AL,[BX]		; READ POSSIBLE OPCODE BYTE
	ADD	DI,AX		; SUM THE TEST BYTES
	ADD	BX,3		; POINT TO POSSIBLE NEXT OPCODE
	LOOP	CKROM		; SUM THE OPCODES
;
	CMP	DI,4*(0E9H)	; SHOULD BE 4  0E9H  OPCODES (JMP)
;
	 IF	INTDVR
	JNZ	OKEXIT		; NO, SO LEAVE DEFAULT DRIVERS
	 ENDIF
;
	 IF	NOT INTDVR
	JNZ	BDEXIT		; NO, SO LEAVE WITH ERROR CONDITION
	 ENDIF
;
	PUSH	CS
	POP	DS		; DS=ES=CS
;
	MOV	SI,offset ROMTAB	; POINT TO SOURCE (ROM CALL TABLE COPY)
	CALL	CPYTAB		; COPY TABLES
;
	DB	FCALL
	DW	3,ROMSEG	; FAR CALL TO ROM "INIT" ROUTINE
;
	MOV	AH,0		; COMMAND FOR CARD TYPE IDENTIFY
;
	DB	FCALL
	DW	6,ROMSEG	; FAR CALL TO DRIVE I/O ROM ENTRY
;
	MOV	CS:CRDTYPE,AL	; SAVE CARD TYPE []
;
	OR	AL,AL		; TEST FOR OMNINET
	JNZ	OKEXIT		; IF FLAT, EXIT
	MOV	AH,4		; SET TO FIND SERVER ADDRESS
	MOV	BX,ABTCTR	; SET ABORT TIME AND RETRYS
;
	DB	FCALL
	DW	6,ROMSEG	; FAR CALL TO DRIVE I/O ROM ENTRY
;
	MOV	CS:BOOTSRVR,AH	; SAVE SERVER #
	MOV	CS:SRVR,AH
	OR	AL,AL		; WAS SERVER # ACTUALLY FOUND
BDEXIT:	MOV	AX,1		; SET FOR ERROR CONDITION
	JNZ	INEXIT		; NO, SO SHOW ERROR AND EXIT
;
OKEXIT:	MOV	AX,0	; RETURN A ZERO
INEXIT:	POP	ES
	POP	DS
;
	IF	LTYPE EQ PASCAL
	RET
	ENDIF
;
	IF	LTYPE EQ BASIC
	PUSH	BP
	MOV	BP,SP
	MOV	BX,6 [BP]	; GET POINTER TO DATA "INTEGER"
	MOV	[BX],AX		; RETURN ERROR CONDITION BYTE
	POP	BP
	RET	2
	ENDIF
;
INITIO	ENDP
;
; --- COPY ADDRESS INFORMATION FROM SOURCE POINTED TO BY DS:SI ---
;
CPYTAB	PROC	NEAR
	MOV	DI,offset LNKTAB	; POINT TO ROUTINE LINKAGE TABLE
	MOV	CX,LENTAB		; SET TO COPY
	REP	MOVSB			; DO COPY
	RET
CPYTAB	ENDP
;
; --- TEST FOR "CORVUS" CONST ][ BIOS ---
;
BIOTST	PROC	NEAR
	MOV	DS,AX		; SET DATA SEGMENT TO THAT OF "BIOS"
	MOV	BX,1		; POINT TO "INIT" ADDRESS FIELD OF JUMP
	MOV	BX,[BX]		; GET THIS ADDRESS IN  BX
	ADD	BX,1		; OFFSET FOR INSTRUCTION SIZE
	MOV	BX,[BX]		; GET POSSIBLE POINTER TO "CORTAb" STRING
;
BIOT1	PROC	NEAR
	MOV	SI,BX		; SAVE IT
	MOV	DI,offset CORTAB	; POINT TO LOCAL COPY OF STRING
	MOV	CX,6		; LENGTH OF STRING
	REPZ	CMPSB		; COMPARE STRINGS
	STC			; SET CARRY TO INDICATE POSSIBLE MISMATCH
	JNZ	BIOE		; EXIT IF MISMATCH
;
	MOV	AX,DS		; GET "BIOS" SEGMENT
	MOV	CL,4		; SET TO MULTIPLY BY 16
	SHL	AX,CL		; CONVERT SEGMENT # TO ADDRESS
	ADD	AX,BX		; FIND "CORTAb" ADDRESS RELATIVE TO SEG. 0
	MOV	CS:CORPTR,AX	; SAVE FOR POSSIBLE USE []
;
	MOV	AL,35 [BX]	; GET "BOOT SERVER" # FROM BIOS
	MOV	CS:BOOTSRVR,AL	; SAVE IT []
	MOV	CS:SRVR,AL	; INIT "DEFAULT SERVER" AS "BOOT SERVER" []
;
	ADD	BX,23		; OFFSET TO ROM FUNCTION TABLE POINTER
	MOV	SI,[BX]		; GET IT
	CALL	CPYTAB		; COPY TABLE INTO THIS DRIVER
	MOV	AH,0		; ID COMMAND
	CALL	far ptr CRVIO	; DO IT
	MOV	CS:CRDTYPE,AL	; SAVE CARD TYPE
	CLC			; CLEAR CARRY TO INDICATE SUCCESS
BIOE:	RET			
;
BIOT1	ENDP
BIOTST	ENDP
;
;
; === RETURN POINTER TO "CORTAb" IN CORVUS BIOS ===
;
	PUBLIC	BIOPTR
;
BIOPTR	PROC	FAR
	MOV	AX,CS:CORPTR	; GET POINTER []
;
	IF	LTYPE EQ PASCAL
	RET
	ENDIF
;
	IF	LTYPE EQ BASIC
	PUSH	BP
	MOV	BP,SP
	MOV	BX,6 [BP]	; GET POINTER TO DATA "INTEGER"
	MOV	[BX],AX		; RETURN POINTER
	POP	BP
	RET	2
	ENDIF
;
BIOPTR	ENDP
;
; ==== SET SERVER # AND READ BOOT SERVER # ====
;
	PUBLIC	SETSRVR
;
SETSRVR	PROC	FAR
	PUSH	BP		; SAVE FRAME POINTER
	MOV	BP,SP		; SET NEW ONE
;
	IF	LTYPE EQ PASCAL
	MOV	CX,6 [BP]	; GET PASSED VALUE
	ENDIF
;
	IF	LTYPE EQ BASIC
	MOV	BX,6 [BP]	; GET POINTER TO VALUE
	MOV	CX,[BX]		; GET ITS VALUE
	ENDIF
;
	OR	CH,CH		; IS IT TOO BIG?
	JNZ	SETS1		; YES, SO DO NOT CHANGE PRESENT VALUE
	MOV	CS:SRVR,CL	; NO, SO SET NEW DEFAULT SERVER #
SETS1:	XOR	AX,AX		; GET A ZERO
	MOV	AL,CS:BOOTSRVR	; GET "BOOT SERVER" # AS RETURN VALUE
;
	IF	LTYPE EQ BASIC
	MOV	[BX],AX		; SET RETURNED VALUE
	ENDIF
;
	POP	BP		; RESTORE FRAME
	RET	2
SETSRVR	ENDP
;
; === FIND A VALID NETWORK SERVER ADDRESS ===
;
	PUBLIC	FINDSRVR
;
FINDSRVR PROC	FAR
	MOV	AH,4		; FIND SERVER COMMAND ( 1.31 bug fix )
	MOV	BX,ABTCTR	; SET MAX RETRY COUNT AND ABORT TIME
	CALL	far ptr CRVIO	; CALL I/O DRIVER
	XCHG	AL,AH		; GET SERVER # IN LSB
;
	IF	LTYPE EQ PASCAL
	RET
	ENDIF
;
	IF	LTYPE EQ BASIC
	PUSH	BP
	MOV	BP,SP
	MOV	BX,6 [BP]	; GET POINTER TO DATA "INTEGER"
	MOV	[BX],AX		; RETURN SERVER #
	POP	BP
	RET	2
	ENDIF
;
FINDSRVR ENDP
;
; === IDENTIFY CORVUS I/O CARD TYPE ===
;
	PUBLIC	CARDID
;
CARDID	PROC	FAR
	MOV	AH,0		; ZERO MSB
	MOV	AL,CS:CRDTYPE	; GET CARD IDENTIFIER
;
	IF	LTYPE EQ PASCAL
	RET
	ENDIF
;
	IF	LTYPE EQ BASIC
	PUSH	BP
	MOV	BP,SP
	MOV	BX,6 [BP]	; GET POINTER TO DATA "INTEGER"
	MOV	[BX],AX		; RETURN CARD TYPE
	POP	BP
	RET	2
	ENDIF
;
CARDID	ENDP
;
; === SEND/RECEIVE A COMMAND TO A NETWORK SERVER ===
;
	PUBLIC	NETCMD
;
NETCMD	PROC	FAR
	PUSH	BP		; SAVE FRAME POINTER
	MOV	BP,SP		; SET NEW ONE
;
	IF	LTYPE EQ PASCAL
	MOV	SI,6 [BP]	; GET ADDRESS OF INPUT STRING
	MOV	DI,8 [BP]	; GET ADDRESS OF OUTPUT STRING
	ENDIF
;
	IF	LTYPE EQ BASIC
	MOV	BX,6 [BP]	; GET ADDRESS OF STRING DESCRIPTOR
	MOV	SI,[BX]		; GET ADDRESS OF INPUT STRING
	MOV	BX,8 [BP]	; GET ADDRESS OF STRING DESCRIPTOR
	MOV	DI,[BX]		; GET ADDRESS OF OUTPUT STRING
	ENDIF
;
	PUSH	DS
	POP	ES		; SET ES=DS (SAVE SEGMENT)
;
	MOV	CX,[SI]		; LOOK AT LENGTH
	MOV	AL,CL		; SAVE FOR RETURN STATUS
	JCXZ	NETE		; IF ZERO, SET RET LENGTH TO ZERO AND RET
;
	PUSH	DI
	INC	SI
	INC	SI		; POINT TO SEND DATA ( DS:SI )
;
	INC	DI
	INC	DI		; POINT TO PLACE TO SAVE RETURNED DATA ( ES:DI)
;
	MOV	DX,530		; SET MAX # OF RETURNED BYTES
;
	MOV	AH,3		; SET FOR  SERVER CMD
	MOV	AL,CS:SRVR	; SET DISK SERVER #
	MOV	BX,ABTCTR	; SET ABORT TIME AND # OF RETRYS
	CALL	far ptr CRVIO	; DO DISK I/O
;
	POP	DI		; GET POINTER BACK TO LENGTH
	MOV	CX,[DI]		; GET LENGTH PREVIOUSLY SET
NETE:	MOV	[DI],CX		; SET LENGTH OF RETURNED STRING
	MOV	AH,0		; CLEAR MSB OF RETURNED VALUE
;
	IF	LTYPE EQ PASCAL
	POP	BP		; GET FRAME POINTER BACK
	RET	4		; CLEAR RETURN STACK
	ENDIF
;
	IF	LTYPE EQ BASIC
	MOV	BX,10 [BP]	; GET POINTER TO DATA "INTEGER"
	MOV	[BX],AX		; RETURN ERROR CONDITION BYTE
	POP	BP
	RET	6
	ENDIF
;
NETCMD	ENDP
;
; === RECEIVE A STRING OF BYTES FROM THE DRIVE ===
;
	PUBLIC	CDRECV, DRVRECV
;
CDRECV	PROC	FAR
DRVRECV:
	PUSH	BP		; SAVE FRAME POINTER
	MOV	BP,SP		; SET NEW ONE
;
	IF	LTYPE EQ PASCAL
	MOV	DI,6 [BP]	; GET ADDRESS OF STRING TO SAVE DATA IN
	ENDIF
;
	IF	LTYPE EQ BASIC
	MOV	BX,6 [BP]	; GET ADDRESS OF STRING DESCRIPTOR
	INC	BX
	INC	BX		; POINT TO STRING POINTER
	MOV	DI,[BX]		; GET ADDRESS OF STRING TO SAVE DATA IN
	ENDIF
;
	PUSH	DS
	POP	ES		; SET ES=DS (SAVE SEGMENT)
;
	PUSH	DI
	PUSH	DS
;
	LDS	SI,CS:dword ptr SNDPTR ; GET POINTER TO SOURCE STRING
	MOV	CX,[SI]		; LOOK AT LENGTH
	MOV	AL,CL		; SAVE FOR RETURN STATUS
	JCXZ	RLPE		; IF ZERO, SET RET LENGTH TO ZERO AND RET
;
	INC	SI
	INC	SI		; POINT TO SEND DATA ( DS:SI )
;
	INC	DI
	INC	DI
	INC	DI		; POINT TO PLACE TO SAVE RETURNED DATA ( ES:DI)
;
	MOV	DX,530		; SET MAX # OF RETURNED BYTES
;
	MOV	AH,1		; SET FOR "BCI" LIKE COMMAND
	MOV	AL,CS:SRVR	; SET DISK SERVER #
	MOV	BX,ABTCTR	; SET ABORT TIME AND # OF RETRYS
	CALL	far ptr CRVIO	; DO DISK I/O
;
RLPE:	POP	DS
	POP	DI		; GET POINTER BACK TO LENGTH
	MOV	[DI],CX		; SET LENGTH OF RETURNED STRING
	MOV	2 [DI],AL	; SAVE RETURN STATUS
	POP	BP		; GET FRAME POINTER BACK
	RET	2		; CLEAR RETURN STACK
CDRECV	ENDP
;
; === SEND STRING OF BYTES TO DRIVE ===
;
;	THIS CONSTELLATION VERSION
;	JUST SAVES TWO POINTERS TO 
;	THE DATA STRING TO SEND.  THE
;	CDRECV  ROUTINE ACTUALLY SENDS
;	THE DATA AND RECEIVES THE
;	RETURN STATUS
;
	PUBLIC	CDSEND, DRVSEND
;
CDSEND	PROC	FAR
DRVSEND:
	PUSH	BP		; SAVE FRAME POINTER
	MOV	BP,SP		; SET NEW ONE
;
	IF	LTYPE EQ PASCAL
	MOV	AX,6 [BP]	; GET ADDRESS OF STRING TO SEND
	ENDIF
;
	IF	LTYPE EQ BASIC
	MOV	BX,6 [BP]	; GET ADDRESS OF STRING DESCRIPTOR
	INC	BX
	INC	BX		; POINT TO STRING POINTER
	MOV	AX,[BX]		; GET ADDRESS OF STRING TO SAVE DATA IN
	ENDIF
;
	MOV	CS:SNDPTR,AX	; SAVE IT []
;
	MOV	AX,DS		; GET DATA SEGMENT
	MOV	CS:SNDSEG,AX	; SAVE IT []
;
	POP	BP		; GET FRAME POINTER BACK
	RET	2		; CLEAR RETURN STACK
CDSEND	ENDP
;
;
;
;
; ============ FLAT CABLE R/W ROUTINES ==============
;
;  THESE ROUTINES ARE ESSENTIALLY THE SAME AS THE FLAT CABLE
;  DRIVERS IN THE "ROM".  THEY ARE REPRODUCED HERE SO THAT
;  SYSTEMS WITH FLAT CABLE INTERFACES NEED NOT HAVE A "ROM"
;  TO WORK WITH  CONSTELLATION ][  SOFTWARE.
;
; --- BUFFERS USED BY "ROM" DRIVER ROUTINES ---
;
CLICKS	DB	0		; BUFFER FOR SAVING # OF CLOCK TICKS
STOPTM	DW	0		; BUFFER FOR SAVING STOP TIME
RMCMD	DB	0		; BUFFER FOR SAVING PASSED "ROM" CMD
BLKLEN	DW	512		; BUFFER FOR SAVING # OF BYTES TO XFER
CMDLEN	DW	4		; BUFFER FOR SAVING LENGTH OF CMD
RTNCODE DB	0		; BUFFER FOR SAVING DISK RETURN CODE
;
; --- SET TIMER ---
;
STIME	PROC	NEAR
	XOR	AH,AH		; READ TIME OF DAY CLOCK
	INT	1AH
	JMP	STIME1
;
; --- CHECK FOR TIMOUT ---
;
CKTIME:	CMP	CS:CLICKS,0	; WAS A WAIT REQUESTED? []
	CLC
	JZ	CKRET		; NO, SO RETURN WITH CARRY CLEAR
	XOR	AH,AH		; TIME OF DAY CALL
	INT	1AH
	OR	AL,AL		; HAS CLOCK WRAPPED AROUND TO ZERO?
	JZ	CKT1		; NO
;
;  IF CLOCK HAS PASSED 24 HOURS, RECALCULATE STOP TIME
;
STIME1:	MOV	AL,CS:CLICKS	; GET # OF CLOCK TICKS OF DELAY []
	XOR	AH,AH
	MOV	CL,4		; SET TO MULTIPLY BY 16
	SHL	AX,CL		; DO IT BY SHIFTING
	ADD	DX,AX
	MOV	CS:STOPTM,DX	; SAVE STOP TIME []
CHKOK:	CLC			; CLEAR CARRY ( TIME CHECK IS OK )
	RET
;
CKT1:	CMP	DX,CS:STOPTM	; TIMEOUT? []
	JB	CHKOK
;
	STC			; SET CARRY IF TIMEOUT
CKRET:	RET
STIME	ENDP
;
; ---- MAIN DRIVER ENTRY POINT ---
;
DRVIO	PROC	FAR
	CLD			; SET FOR "INCREMENT"
	MOV	CS:RMCMD,AH	; SAVE COMMAND []
	CMP	AH,1		; IS IT A "BCI" COMMAND?
	JZ	RW15		; YES, SO DO IT
	CMP	AH,5		; IS IT A "WRITE" COMMAND?
	JZ	RW15		; YES, SO DO IT
	CMP	AH,0		; IS IT AN "IDENTIFY" COMMAND
	JZ	RW00		; YES, SO DO IT
	MOV	AL,0FFH		; IF ANY OTHER, INDICATE "ABORT" OR ERROR
	MOV	CX,0		
	RET			; LONG RET
;
RW00:	MOV	AL,1		; INDICATE FLAT CABLE
	RET			; LONG RET
;
;
RW15:	PUSH	DS		; SAVE REGISTERS THAT MAY BE CHANGED
	PUSH	SI
	PUSH	DI
	PUSH	BX
	PUSH	DX
;
	MOV	CS:CLICKS,BL	; SAVE # OF TIMER CLICKS  []
	MOV	CS:BLKLEN,DX	; SAVE BLOCK LENGTH []
	MOV	CS:CMDLEN,CX	; SAVE CMD LENGTH []
	CALL	ROMIO		; DO DISK I/O
	POP	DX
	POP	BX
	POP	DI
	POP	SI
	POP	DS
	MOV	AL,CS:RTNCODE	; GET RETURN CODE []
;
DMYLRET	LABEL	FAR
	RET			; LONG RET
;
DMYIRET	LABEL	FAR
	IRET			; DUMMY  "IRET"
;
DRVIO	ENDP
;
ROMIO	PROC	NEAR
	CALL	STIME		; SETUP TIMER COUNT
;
RO1:	MOV	DX,STAT		; POINT TO STATUS PORT
	CLI			; DISABLE INTERRUPTS FOR TEST
	IN	AL,DX		; READ DRIVE STATUS BYTE
	TEST	AL,DRDY		; IS IT READY?
	JZ	RO2		; YES, SO PROCEED
	STI			; NO, SO RE-ENABLE INTERRUPTS
	CALL	CKTIME		; CHECK IF TIMED OUT
	JNC	RO1		; IF NOT, TRY AGAIN
ARET:	MOV	CS:RTNCODE,0FFH ; IF TIMED OUT, SET ERROR []
	MOV	CX,0		; INDICATE NO DATA RETURNED
	RET
;
RO2:	MOV	CX,CS:CMDLEN	; GET CMD LENGTH []
	CALL	SNDBLK		; SEND BLOCK OF DATA TO DRIVE
	CMP	CS:RMCMD,5	; WAS CMD A "WRITE" CMD? []
	JNZ	RCVBLK		; NO, SO GO RECEIVE DATA
;
	MOV	SI,DI		; YES, POINT TO SECTOR DATA
	MOV	AX,ES
	MOV	DS,AX
	MOV	CX,CS:BLKLEN	; GET LENGTH OF DATA BLOCK []
	CALL	SNDBLK		; SEND SECTOR DATA
;
RCVBLK:	CALL	STIME		; SET TIMER
;
	CALL	DELAY1		; DELAY
;
RCV1:	CALL	CKTIME		; TIMED OUT YET?
	JC	ARET		; YES, SO RETURN WITH ERROR
;
RCV2:	MOV	DX,STAT		; POINT TO STATUS PORT
	IN	AL,DX		; READ DRIVE STATUS BYTE
	TEST	AL,DIFAC	; TEST BUS DIRECTION
	JNZ	RCV1		; WAIT FOR "HOST TO DRIVE"
	TEST	AL,DRDY		; TEST IF ALSO READY
	JNZ	RCV1
;
	CALL	DELAY1		; WAIT TO BE SURE
;
	IN	AL,DX		; TEST STATUS AGAIN
	TEST	AL,DIFAC		
	JNZ	RCV1		; IF FALSE ALARM, TRY AGAIN
	TEST	AL,DRDY
	JNZ	RCV1		; IF NOT READY, TRY AGAIN
;
	DEC	DX		; POINT TO DATA PORT
	IN	AL,DX		; GET RETURN CODE
	INC	DX		; POINT BACK TO STATUS PORT
;
	MOV	CX,1		; INDICATE 1 BYTE WAS RETURNED
	MOV	CS:RTNCODE,AL	; SAVE IT []
	CMP	CS:RMCMD,5	; WAS CMD A "WRITE" CMD []
	JZ	RCRET		; YES, SO RETURN
;
	MOV	BX,CX		; OTHERWISE SET COUNTER
	MOV	CX,CS:BLKLEN	; GET LENGTH OF EXPECTED DATA
;
RCV3:	IN	AL,DX		; GET STATUS AGAIN
	TEST	AL,DRDY		; IS DRIVE READY?
	JNZ	RCV3		; NO, SO WAIT
	TEST	AL,DIFAC	; ARE WE DONE?
	JNZ	RCV4		; POSSIBLY, ...
;
	DEC	DX		; POINT TO DATA PORT
	IN	AL,DX		; GET DATA FROM DRIVE
	INC	DX		; POINT BACK TO STATUS PORT
;
	JCXZ	RCVS		; IF DATA NOT WANTED
	STOSB			; SAVE DATA IN BUFFER
	DEC	CX		; COUNT DOWN # TO SAVE
;
RCVS:	INC	BX		; COUNT UP # RECEIVED
	JMP	RCV3		; LOOP UNTIL EXIT
;
RCV4:	IN	AL,DX		; GET STATUS BYTE
	TEST	AL,DRDY		; IS DRIVE READY
	JNZ	RCV3		; NO, SO PREVIOUS RESULT MAY BE FALSE
	TEST	AL,DIFAC	; IS IT STILL "HOST TO DRIVE"?
	JZ	RCV3		; NO, SO TRY AGAIN
;
	MOV	CX,BX		; GET # OF BYTES RECEIVED
RCRET:	RET
;
DELAY1:	MOV	BL,15		; SET DELAY
DELAY:	DEC	BL
	JNZ	DELAY		; LOOP UNTIL DONE
	RET
;
; --- SEND BLOCK OF DATA TO DRIVE ---
;
SNDBLK:	MOV	DX,STAT		; POINT TO STATUS PORT
;
SND1:	IN	AL,DX		; GET STATUS BYTE
	TEST	AL,DRDY		; IS DRIVE READY?
	JNZ	SND1		; NO, SO LOOP
;
	DEC	DX		; POINT TO DATA PORT
	LODSB			; GET DATA FROM MEMORY
	OUT	DX,AL		; SEND DATA TO DRIVE
	INC	DX		; POINT BACK TO STATUS PORT
;
	STI			; RE-ENABLE INTERRUPTS
	LOOP	SND1		; CONTINUE UNTIL DONE
	RET
;
ROMIO	ENDP
;
;
; ---- INTERFACE "FAR" CALL TABLE ---
;	THIS TABLE GETS PATCHED
;	TO EITHER "BIOS" CALLS OR
;	"ROM" CALLS IF THE APPROPRIATE
;	       LINK IS FOUND
;
LNKTAB	PROC	NEAR
	JMP	DMYLRET		;
	JMP	DMYLRET		;
;
CRVIO	LABEL	FAR
	JMP	DRVIO		; THIS SHOULD BE A FAR CALL
;
	JMP	DMYIRET		; THIS SHOULD BE A FAR JUMP
LNKTAB	ENDP
;
; =========================================================
;
PGSEG	ENDS
;
;
	END
