								     COMMENT ~
FILES.ASM -- File manipulation Procedures
 
   From `BLUEBOOK of ASSEMBLY ROUTINES for the IBM PC & XT'
         by Christopher L. Morgan
         Copyright (C) 1984 by The Waite Group, Inc.
 
   Contents:
   ---------
   CBUF_CLR	--  Clear the circular buffer
   CBUF_IN	--  Input to circular buffer 
   CBUF_INIT	--  Initialize a circular buffer
   CBUF_OUT	--  Output to a circular buffer
   CBUF_PUT	--  Put a byte into a circular buffer
   CLOS_FIL	--  Close a file
   EMSG_OUT	--  Exception message output
   FILT_CR*	--  Filter for carriage return/linefeed
   MAKE_FIL	--  Create a file
   READ_FIL	--  Read bytes from a file
   SAVE		--  Save a file from communications line to disk
   WRIT_FIL	--  Write bytes to a file
 
    >>>>>  See FILES.DOC for complete descriptions of these routines. <<<<<
 ------------------------------------------------------------------------------
  It is better to put this data in the source code calling these routines, and
    then comment out this section.					      ~
DATAS	SEGMENT	PUBLIC
	CR	EQU	0DH			;ASCII code for carriage return
	LF	EQU	0AH			;ASCII code for linefeed
	CTRL_Z	EQU	1AH			;ASCII code for ^Z
	BLK_SIZ   =	0			;K=block size
	CBUF_SIZ  	DW	256             ;K=CBUF max capacity
	CBUF	 	DB	1		;K=base address of CBUF
	LBUF		DB	256 DUP('LBUF')
	NAME_BUF	DB	64 DUP('NAME')
;
;Following table used by EMSG_OUT
	EMSG	DW	EMSG1
		DW	EMSG2
		DW	EMSG3
		DW	EMSG4
		DW	EMSG5
		DW	EMSG6
		DW	EMSG7
		DW	EMSG8
		DW	EMSG9
		DW	EMSG10
		DW	EMSG11
		DW	EMSG12
		DW	EMSG13
		DW	EMSG14
		DW	EMSG15
		DW	EMSG16
		DW	EMSG17
		DW	EMSG18
	EMSG1	DB	CR,LF,'Invalid function number',CR,LF,0 
	EMSG2	DB	CR,LF,'File not found',CR,LF,0
	EMSG3	DB	CR,LF,'Path not found',CR,LF,0
	EMSG4	DB	CR,LF,'Too many open files',CR,LF,0 
	EMSG5	DB	CR,LF,'Access denied',CR,LF,0
	EMSG6	DB	CR,LF,'Invalid handle',CR,LF,0
	EMSG7	DB	CR,LF,'Memory control blocks destroyed',CR,LF,0
	EMSG8	DB	CR,LF,'Insufficient memory',CR,LF,0
	EMSG9	DB	CR,LF,'Invalid memory block address',CR,LF,0
	EMSG10  DB	CR,LF,'Invalid environment',CR,LF,0
	EMSG11	DB	CR,LF,'Invalid format',CR,LF,0
	EMSG12	DB	CR,LF,'Invalid access code',CR,LF,0
	EMSG13	DB	CR,LF,'Invalid data',CR,LF,0
	EMSG14	DB	CR,LF,'Message not in use',CR,LF,0
	EMSG15	DB	CR,LF,'Invalid drive was specified',CR,LF,0
	EMSG16	DB	CR,LF,'Attempted to remove the current directory',CR,LF,0
	EMSG17	DB	CR,LF,'Not same device',CR,LF,0
	EMSG18	DB	CR,LF,'No more files',CR,LF,0
DATAS	ENDS 
;------------------------------------------------------------------------------
;It is better to include this data in the source code calling these routines, 
;  and then comment out this section.		
STACKS	SEGMENT	STACK
	DB	20 DUP(' STACKS ')
STACKS	ENDS
;------------------------------------------------------------------------------
CODES	SEGMENT
;The following variables/constants must be defined in the source code calling
;  these routines. See FILES.DOC.

	EXTRN	COM_INCK:FAR,COM_INIT:FAR,COM_ON:FAR,COM_OFF:FAR,MSG_OUT:FAR
	EXTRN	DTA:DWORD,LAST:BYTE,GAUGE:WORD,NEARFULL:WORD,NEAREMPTY:BYTE

	PUBLIC	EMSG_OUT,GET_SPEC,MAKE_FIL,CLOS_FIL,WRIT_FIL,READ_FIL
	PUBLIC	CBUF_INIT,CBUF_PUT,CBUF_IN,CBUF_CLR,CBUF_OUT,SAVE

ASSUME CS:CODES,DS:DATAS
;----------------------------I/O ROUTINES--------------------------------------
;Routine to send out exception messages
EMSG_OUT	PROC	FAR
	PUSH	SI				;Save registers
	PUSH	AX
	ADD	AX,AX				;Double to index through table
	MOV	SI,AX				;SI points into table
	MOV	SI,EMSG[SI]			;Look up address of message
	CALL 	MSG_OUT				;Send the message
	POP	AX				;Restore registers
	POP	SI
	RET
EMSG_OUT	ENDP
;------------------------------------------------------------------------------
;Routine to get a file specifier 
;
GET_SPEC	PROC	FAR
	PUSH	DS				;Save registers
	PUSH	ES
	PUSH	SI
	PUSH	DI
	PUSH	CX
;
;Set up pointer to DTA	to get parameters
	LDS	SI,DTA				;Point to DTA for parameters
	MOV	CL,[SI]				;Get length of string
	MOV	CH,0				;Make 16-bit
	INC	SI				;Skip the length byte
	MOV	AL,' '				;Scan past the spaces	
GET_SPEC1:
	CMP	[SI],AL				;Check for space
	JNE	GET_SPEC2			;Exit loop if nonspace
	INC	SI				;Else point to next byte
	LOOP	GET_SPEC1			;Loop back for more
	JCXZ	GET_SPEC3			;No file specifier?
;
;Move the rest into place
GET_SPEC2:
	MOV	DI,DX				;Index points to destination
	CLD					;Forward direction
	REP	MOVSB				;Make the transfer
	CLC					;No error, so no carry
	JMP	GET_SPEC_XIT			; and return
GET_SPEC3:
	MOV	AX,20				;No file specified
	STC					;Set carry for error
	JMP	GET_SPEC_XIT			; and exit
GET_SPEC_XIT:
	POP	CX				;Restore registers
	POP	DI
	POP	SI
	POP	ES
	POP	DS
	RET
GET_SPEC	ENDP
;------------------------------------------------------------------------------
;Routine to create a file
;
MAKE_FIL	PROC	FAR
	PUSH	CX				;Save register
	MOV	CX,0				;Attribute 0
	INT	21H				;DOS call
	POP	CX				;Restore register
	RET
MAKE_FIL	ENDP
;------------------------------------------------------------------------------
;Routine to close a file 
;
CLOS_FIL	PROC	FAR
	MOV	AH,3EH				;Close a file
	INT	21H				;DOS call
	RET
CLOS_FIL	ENDP
;------------------------------------------------------------------------------
;Routine to write bytes to a file
;
WRIT_FIL	PROC	FAR
	MOV	AH,40H				;Write to a file
	INT	21H				;DOS call
	CMP	AX,CX				;Was it all written?
	JE	WRIT_FIL_XIT			;Skip if yes
	MOV	AX,21				;Not all bytes were transferred
	STC					;Set carry for error
WRIT_FIL_XIT:
	RET
WRIT_FIL	ENDP
;------------------------------------------------------------------------------
;Routine to read bytes from a file
;
READ_FIL	PROC	FAR
	MOV	AH,3FH				;Read from a file
	INT	21H				;DOS call
	CMP	AX,CX				;Everything back?
	JE	READFIL_XIT			;Skip if yes
	MOV	AX,22				;Not all read
	STC					;Set carry for error
READFIL_XIT:
	RET
READ_FIL	ENDP
;------------------------------------------------------------------------------
;Routine to handle input for a circular buffer 
;
CBUF_INIT	PROC	FAR
	MOV	SI,0				;Initialize SI
	MOV	DI,0				;Initialize DI
	MOV	GAUGE,0				; and # of bytes in buffer
	MOV	LAST,0				; and last byte buffer
	RET
CBUF_INIT	ENDP
;------------------------------------------------------------------------------
;Routine to one byte into a circular buffer 
;
CBUF_PUT	PROC	FAR
	PUSH	DX				;Save register
	MOV	DX,GAUGE			;Put V Guage in DX
	MOV	CBUF[DI],AL			;Put the byte into the buffer
	INC	DI				;Adjust pointer to next char
	CMP	DI,CBUF_SIZ			;Wrap it around?
	JNE	CBUF_PUT1			;Skip if no wrap
	MOV	DI,0				;Wraps back to zero
CBUF_PUT1:
	INC	DX				;Count the character
	CMP	DX,NEARFULL			;Too many characters?
	JNE	CBUF_PUT_XIT			;Skip if not
	CALL 	COM_OFF				;Request to stop flow
CBUF_PUT_XIT:
	POP	DX				;Restore register
	RET
CBUF_PUT	ENDP
;------------------------------------------------------------------------------
;Routine to handle input to a circular buffer
;
CBUF_IN	PROC	FAR
	CALL	COM_INCK			;Check for a character
	JZ	CBUF_IN_XIT			;Good exit if none
	AND	AL,7FH				;Strip off parity bit
	CMP	AL,10				;Check for <LF> (ASCII 10)
	JNE	CBUF_IN1			;Skip if not <LF>
	CMP	LAST,13				;Check for <CR> (ASCI 13)
	JE	CBUF_IN2			;Skip if CR/LF
CBUF_IN1:
	CALL	CBUF_PUT			;Put byte into buffer
	MOV	LAST,AL				;Update last byte
CBUF_IN2:
	CMP	AL,13				;Check for <CR>
	JNE	CBUF_IN3			;Skip if not
;
;Insert LF if character was <CR>
	PUSH	AX				;Save the current character
	MOV	AL,10				;<LF>
	CALL	CBUF_PUT			; goes into buffer
	POP	AX				;Restore current character
;
;Check for end of file
CBUF_IN3:
	CMP	AL,CTRL_Z			;Check for ^-Z (1AH)
	JE	CBUF_IN5			;EOF is an exception
;
;Check for overflow
	PUSH	DX				;Save register
	MOV	DX,GAUGE			
	CMP	DX,NEARFULL			;Overflow of characters?
	JL	CBUF_IN4			;Skip if not
	CALL	COM_OFF				;Stop flow request
	JMP	CBUF_IN_XIT			;Good exit
CBUF_IN4:					;No overflow
	CALL	COM_ON				;Make sure flow is on
	JMP	CBUF_IN_XIT			;Good exit
CBUF_IN5:					;EOF exception handling
	MOV	AX,19				;EOF exception code
	STC					;Set carry for exception
	JMP	CBUF_IN_RET			;Return with exception
CBUF_IN_XIT:
	POP	DX
	CLC					;Clear carry -- no exception
CBUF_IN_RET:
	RET
CBUF_IN	ENDP
;------------------------------------------------------------------------------
;Routine to flush a circular buffer 
;
CBUF_CLR	PROC	FAR
	PUSH	DI				;Save registers
	PUSH	DX
	PUSH	CX
;
;Get the bytes from the buffer
	MOV	CX,GAUGE			;Get the count
	JCXZ	CBUF_CLR_XIT			;Check for empty
	MOV	DI,0				;Initialize destination
;
;Loop to get all the bytes
CBUF_CLR1:
	MOV	AL,CBUF[SI]			;Get the character
	INC	SI				;Increment the pointer
	CMP	SI,CBUF_SIZ			;Wrap it?
	JNE	CBUF_CLR2			;Skip if no wrap
	MOV	SI,0				;Wrap the buffer pointer
CBUF_CLR2:
	MOV	LBUF[DI],AL			;Put the byte in linear buffer
	INC	DI
	LOOP	CBUF_CLR1			;Loop until all bytes in LBUF
;
;Send the linear buffer to the disk
	MOV	CX,GAUGE			;This is the number of bytes
	LEA	DX,LBUF				;Here is where they are
	CALL	WRIT_FIL			;Send them out
	MOV	GAUGE,0				;Set circular buffer empty
CBUF_CLR_XIT:
	POP	CX				;Restore registers
	POP	DX	
	POP	DI
	RET
CBUF_CLR	ENDP
;------------------------------------------------------------------------------
;Routine to handle output from a circular buffer 
;
CBUF_OUT	PROC	FAR
	CMP	GAUGE,BLK_SIZ			;Any characters?
	JL	CBUF_OUT_XIT			;If no, go
;
;Empty the buffer
	CALL	COM_OFF				;Hold the com line
	CALL	CBUF_CLR			;Clear the buffer
	JC	CBUF_OUT_RET			;Error?
CBUF_OUT_XIT:
	CLC					;Clear carry -- no exception
CBUF_OUT_RET:
	RET
CBUF_OUT	ENDP
;------------------------------------------------------------------------------
;Routine to save a file from a communications line to disk 
;
SAVE	PROC	FAR
;
;Set up return
	PUSH	DS				;Save for proper return
	MOV	AX,0				;Point to beginning of segment
	PUSH	AX				; for the offset
;
;Set up segments
	MOV	DX,DS				;PSP was data segment
	MOV	AX,DATAS			;New data segment
	MOV	DS,AX				;Put in DS
	MOV	ES,AX				; and in ES
	MOV	WORD PTR DTA+2,DX		;Set the segment of the data
;
;Set up the file
	LEA	DX,NAME_BUF			;Point to the ASCIIZ buffer
	CALL	GET_SPEC			;Get file specifications
	JC	EXCEPTION			;Error?
	LEA	DX,NAME_BUF			;ASCIIZ buff had file specs
	CALL	MAKE_FIL			;Set up the file
	JC	EXCEPTION			;Error?
	MOV	BX,AX				;Get the file handle
;
;Set up the communications line
	MOV	DX,1				;For COM1:
	MOV	AL,0BBH				;2400,E,8,1
	CALL	COM_INIT			;Initialize it
	CALL	COM_ON				; & turn it on
	CALL	CBUF_INIT			;Initialize the circular buffer
	MOV	DX,1				;COM_2:
;
;Main loop for pulling in bytes
SAV_LOOP:
	CALL	CBUF_IN				;Check for input
	JC	EXCEPTION			;Error or EOF?
	CALL	CBUF_OUT			;Check to send it out
	JC	EXCEPTION			;Error?
	JMP	SAV_LOOP
EXCEPTION:					;Error/Exception handler
	CMP	AX,19				;EOF?
	JE	SAV_1				;If so, close it up & return
	CALL	EMSG_OUT			;Else report exception
	JMP	SAV_XIT				; and return
SAV_1:						;Normal return
	CALL	COM_OFF				;Com line off
	CALL	CBUF_CLR			;Clear the circular buffer
	JC	EXCEPTION			;Error?
	CALL	CLOS_FIL			;Close the file
	JC	EXCEPTION			;Error?
	JMP	SAV_XIT				;Exit
SAV_XIT:
	CALL	COM_ON				;Turn on com line
	RET	
SAVE	ENDP
;------------------------------------------------------------------------------
CODES	ENDS
;
	END
;_____________________________________________________________________________
;>>>>> Physical EOF FILES.ASM <<<<<
