;-------------------------------------------------------
;	CPLDRV.ASM
;	Careplane SDN assembler functions
;
;	- CplInit
;	- CplWrite
;	- CplRead
;	- CplExit
;
;	MSc IT project
;	Grant Forrest
;	Dept of Computing & Electrical Engineering
;	Heriot-Watt University, Edinburgh
;	Modified from original careplane driver by N.Antoniak (HP) 1987
;-------------------------------------------------------
;
;-------------------------------------------------------
;       Set up SHARED MEMORY CONTROL BLOCK
;	Contains information about the position, size,
;       and status of the receive and transmit channels.
;	Size = 32 words, offsets as follows:
;-------------------------------------------------------


SHM_SIZE	EQU	0	;size of complete area in words
SHM_FLAGS	EQU	2       ;flags
XMIT_START	EQU	4	;transmit buffer start
XMIT_SEM	EQU	6	;transmit buffer semaphore
XMIT_SIZE	EQU	8	;transmit buffer size
XMIT_COUNT	EQU	10	;no.of words currently in tbuffer
XMIT_FIRST	EQU	12	;position of 1st word rel to tbuffer
XMIT_NEXT	EQU	14	;position of next free word rel to tbuffer
RECV_START	EQU	16	;starting position of rbuffer
RECV_SEM	EQU	18	;receive buffer semaphore
RECV_SIZE	EQU	20	;receive buffer size
RECV_COUNT	EQU	22	;no.of words currently in rbuffer
RECV_FIRST	EQU	24	;position of first word relative to rbuffer
RECV_NEXT	EQU	26	;position of next free word rel to rbuffer

;---------------------------------------------------------
;	Define SEGMENT REGISTER ADDRESS
;	DMA CHANNEL = 5
;---------------------------------------------------------


CPL_PORT	EQU	380H    ;address of interface card
DMA_MODE	EQU	0D6H	;mode register DMA2
DMA_MASK	EQU	0D4H	;mask register DMA2
MODE_SET	EQU	0C1C1H	;mode byte to cascade DMA-controller 2
MASK_SET	EQU	0101H	;enable DMA channel 5
MASK_RESET	EQU	0505H	;

;---------------------------------------------------------------------

                .MODEL MEDIUM	;Allow > 1 code segment

;---------------------------------------------------------------------

		.DATA?		;Uninitialised data segment

	        _shm_seg	DW	?
		recv_lastb	DW	?
		xmit_lastb	DW	?

;---------------------------------------------------------

		.CODE


		PUBLIC	_CplInit	;Available to other modules
		PUBLIC	_CplWrite
		PUBLIC	_CplRead
		PUBLIC	_CplExit

;=========================================================
;
;	CPLINIT
;
;       unsigned xmitbufsize, recvbufsize
;	unsigned CplInit (xmitbufsize, recvbufsize)
;
;	arguments:	size of transmit buffer (words)
;			size of receive buffer (words)
;
;	returns:	shared memory segment address
;			or 0 on error
;
;=========================================================

_CplInit	PROC FAR
		PUSH BP		;save caller's BP
                MOV BP,SP
                PUSH ES


                ;Allocate Memory
                ;---------------

                MOV BX,WORD PTR [BP+6]	;size of transmit buffer in words
                ADD BX,WORD PTR [BP+8]	;plus size of receive buffer
                ADD BX,32		;plus size of shared memory control
                			; block in words
		MOV DX,BX		;DX contains total size in words

                MOV CL,3		;8 words = 1 paragraph
                SHR BX,CL		;total size in paragraphs
                INC BX			; plus 1

                MOV AH,48H		;DOS call to allocate memory
                INT 21H			;segment address returned in AX
                JNC Allocated		;No carry, allocation OK

                XOR AX,AX		;Error! Clear AX
                MOV _shm_seg,0		; segment = 0
                JMP i_exit		; abort procedure

     Allocated: MOV _shm_seg,AX		;Success, AX=segment address


                ;Initialise Shared Memory Control Block (SMCB)
                ;---------------------------------------------

                MOV ES,AX		;ES contains shm address

                MOV ES:[SHM_SIZE],DX	;put total size(words) into SMCB

                DEC DX			;last word of transmit buffer
                SHL DX,1		;offset in BYTES
                MOV xmit_lastb,DX	;

                XOR DX,DX		;Clear DX
                MOV ES:[SHM_FLAGS],DX	;Initialise remainder of SMCB

                MOV ES:[RECV_SEM],DX	;Initialise Receive buffer
                MOV ES:[RECV_COUNT],DX
                MOV ES:[RECV_FIRST],DX
                MOV ES:[RECV_NEXT],DX

                MOV ES:[XMIT_SEM],DX	;Initialise Transmit buffer
                MOV ES:[XMIT_COUNT],DX
                MOV ES:[XMIT_FIRST],DX
                MOV ES:[XMIT_NEXT],DX

                MOV BX,32		;size of SMCB
                MOV CX,WORD PTR [BP+6]	;size of transmit buffer
                MOV DX,WORD PTR [BP+8]	;size of receive buffer

                MOV ES:[RECV_START],BX	;Initialise start and end addresses
                MOV ES:[RECV_SIZE],DX

                MOV ES:[XMIT_START],BX
                ADD ES:[XMIT_START],DX
                MOV ES:[XMIT_SIZE],CX

		MOV BX,ES:[XMIT_START]	;last word in receive buffer
                DEC BX			; is the first word in the transmit
                SHL BX,1		; buffer minus 1, conv to bytes
                MOV recv_lastb,BX


		;Initialise DMA Controller
                ;------------------------------------

                MOV AX,MODE_SET		;mode byte to cascade
                OUT DMA_MODE,AX

                MOV AX,MASK_SET         ;enable DMA channel 5
                OUT DMA_MASK,AX



                ;Reset Segment Register by writing in 0000H
                ;------------------------------------------


                MOV DX,CPL_PORT
                XOR AX,AX
                OUT DX,AX



                ;Wait for about (!) 100msec
                ;--------------------------

        	MOV AX,0FFFFH
        i_loop: DEC AX
                JNZ i_loop


                ;Write Correct Segment Number of Shared Memory
		;into the Segment Register
		;---------------------------------------------


		MOV AX,_shm_seg
                MOV DX,CPL_PORT
                OUT DX,AX


        i_exit:	POP ES
        	MOV SP,BP
                POP BP

                RET
 _CplInit	ENDP

;======================================================
;
;	CPLWRITE
;
;       byte *address
;	unsigned n
;	void CplWrite (address, n)
;
;	arguments:	address of Message
;			number of words to write
;	returns:	nil
;
;======================================================

_CplWrite	PROC FAR

                PUSH BP
                MOV BP,SP
                PUSH DI
                PUSH SI
                PUSH ES

                MOV ES,_shm_seg

                MOV DI,ES:[XMIT_START]	;start of transmit buffer
                ADD DI,ES:[XMIT_NEXT]	;next free word
                SHL DI,1		;offset in bytes

                MOV SI,WORD PTR [BP+6]	;address of request
                MOV CX,WORD PTR [BP+8]	;no. of words

                MOV DX,CX
                CLD     		;increment SI with each LODS


	w_wait:	MOV AX,0FFFFH		;set all 16 bits of XMIT_SEM
        				; (can use 8000H, as only bit 15
		XCHG AX,ES:[XMIT_SEM]	; is required)
		CMP AX,0                ;wait for XMIT_SEM to unlock
                JNE w_wait		; XCHG locks it with 0xFFFF


      do_write:	LODSW     		;get word from address in DI
      		XCHG AH,AL		; reverse the bytes
                STOSW			;write to transmit buffer
                CMP DI,xmit_lastb	;last byte reached?
                JBE w_next		; no, write next word
                MOV DI,ES:[XMIT_START]	; yes, point to start of buffer
                SHL DI,1		;offset in bytes
        w_next: LOOP do_write

                SHR DI,1		;current offset in words
                SUB DI,ES:[XMIT_START]	;subtract starting offset
                MOV ES:[XMIT_NEXT],DI	;move pointer to next free word
                			; in buffer
		ADD ES:[XMIT_COUNT],DX	;update the no. of words currently
                			; in the buffer

                MOV ES:WORD PTR [XMIT_SEM],0	;clear the semaphore


                POP ES
                POP SI
                POP DI
                MOV SP,BP
                POP BP
                RET
_CplWrite	ENDP


;=========================================================
;
;	CPLREAD
;
;       byte *address
;	unsigned n
;	unsigned CplRead (address,n)
;
;	arguments:	address of Buffer[] to read TO
;			number of words to read or 0
;	returns:	number of words read
;
;	Timeout if next word not read within defined period
;
;==========================================================

_CplRead	PROC FAR

		PUSH BP
                MOV BP,SP
                PUSH DI
                PUSH SI
                PUSH DS
                PUSH ES

                PUSH DS
                POP ES			;ES now has address of DS


                MOV DS,_shm_seg
                MOV SI,DS:[RECV_START]	;start of receive buffer
                ADD SI,DS:[RECV_FIRST]	; plus address of 1st word
                SHL SI,1		; in bytes

                MOV DI,WORD PTR [BP+6]	;buffer address passed (write to)
                MOV DX,WORD PTR [BP+8]	;no.of words to read
                XOR CX,CX		;no.read so far (0)
                CLD			;increment SI after each read
                MOV BX,100		;TimeOut value


        r_wait:	DEC BX
                JE r_no_data		;TimeOut -> no data in buffer
                MOV AX,0FFFFH
                XCHG AX,DS:[RECV_SEM]	;check semaphore
                CMP AX,0		; locked?
                JNE r_wait		;  yes,decrement timer and loop

                CMP DX,0		;No.of words to read given?
                JNE r_compare           ; Yes
                MOV DX,DS:[RECV_COUNT]	; No, read all in receive buffer
                JMP r_do

     r_compare:	CMP DX,DS:[RECV_COUNT]	;Compare no.of words to read with
     					; no.in buffer
                JG r_end		;Not enough words in buffer -> exit

          r_do:	CMP CX,DX		;read completed?
          	JE r_exit		; yes,exit

                LODSW
                MOV WORD PTR [SI-2],0000H	;clear previous word
                XCHG AH,AL			;reverse the bytes
                STOSW				;store in Buffer[]
                				;
                CMP SI,SS:recv_lastb		;reached end of buffer?
                JBE r_next			; no, read next
                MOV SI,DS:[RECV_START]		; yes,point back to start
                SHL SI,1			; convert to bytes

        r_next: INC CX			;no.of words read
                JMP r_do		;next read

        r_exit: SUB DS:[RECV_COUNT],CX	;update no.of words received
        	SHR SI,1		;convert to words
                SUB SI,DS:[RECV_START]	;point SI back to start
                MOV DS:[RECV_FIRST],SI	;first free slot = start

         r_end:	MOV DS:WORD PTR [RECV_SEM],0	;Unlock semaphore

     r_no_data: MOV AX,CX		;Return no.of words read


     		POP ES
                POP DS
                POP SI
                POP DI
                MOV SP,BP
                POP BP
                RET

_CplRead	ENDP


;==================================
;
;	CPLEXIT
;
;	void cplexit (void);
;	arguments: 	nil
;	returns:	nil
;
;===================================


_CplExit	PROC FAR
		PUSH BP
                MOV BP,SP
                PUSH ES


                ;Free Allocated Memory
                ;---------------------


                MOV AH,49H		;DOS free allocated memory
                MOV ES,_shm_seg		;address of shared memory
                INT 21H

                ;Clear Shared Memory Segment Register
                ;------------------------------------


                XOR AX,AX
                MOV DX,CPL_PORT
                OUT DX,AX

		MOV AX,5000		;give careplane time to
	e_wait:	DEC AX			; detect change
        	JNZ e_wait

                MOV AX,MASK_RESET	;Reset DMA channel
                OUT DMA_MASK,AX


                POP ES
                MOV SP,BP
                POP BP

                RET
_CplExit	ENDP

;------------------------------------------------------

        	END