	TTL	"Port-Handler AmigaDos Device Driver"
* Port-handler AmigaDos Device Driver
* Copyright (c) 1986 John A. Toebes, VIII   All Rights Reserved
*
* Permission is granted for personal usage only.
* This program may not be incorporated in any commercial product or sold
* for any purpose without the permission of the author.
*
		SECTION	CODE
StartModule	DC.L	(EndModule-StartModule)/4
EntryPt		EQU	*
*
	NOLIST
	INCLUDE	"exec/types.i"
	INCLUDE	"exec/libraries.i"
	INCLUDE	"exec/io.i"
	INCLUDE	"libraries/dosextens.i"
	LIST
	INCLUDE "bcpl.i"
***
***	Global constants used
***
FALSE	EQU	0
TRUE	EQU	-1
IOB_size	EQU	30
RAW_NAME	EQU	('R'<<24)!('A'<<16)!('W'<<8)
MARKER1_DATA	EQU	('T'<<24)!('O'<<16)!('P'<<8)!('!')
MARKER2_DATA	EQU	('J'<<24)!('O'<<16)!('H'<<8)!('N')

***
***	This is the global structure that we get a pointer to in A1
***	when our code is executing
***
	STRUCTURE	PORT_HANDLER_GLOBALS,0
	LONG	IO_parm_pkt		*Pointer to our initial packet
*	LONG	MARKER1
*	LONG	EXECLIB
*	LONG	OURCODE
*	LONG	DOSCALL
*	LONG	DOSRET
	LONG	read_pkt		*Pointer to user read request
	LONG	write_pkt		*Pointer to user write request
	BYTE	openin			*device is open for input
	BYTE	openout			*device is open for output
	WORD	FILLER			* to pad to a 4 byte boundary
	BPTR	node			*pointer to our DeviceList node
	BPTR	inpkt			*pointer to read transmission pkt
	STRUCT	inpkt_buf,16		* Buffer for above
	BPTR	outpkt			*pointer to write transmission pkt
	STRUCT	outpkt_buf,16		* Buffer for above
	BPTR	IOB			*IO request for reading from device
	STRUCT	IOB_buf,(IOB_size*4)+8	* Buffer for above
	BPTR	IOBO			*IO request for writing to device
	STRUCT	IOBO_buf,(IOB_size*4)+8	* Buffer for above
	APTR	dataloc			*QUE pointer-data to be written/read
	BPTR	tpkt			*QUE pointer-IO completion msg packet
	BPTR	use_IOB			*QUE pointer-IOB for IO
*	LONG	MARKER2
	BPTR	FILLER1
	LABEL	FRAME_SIZE
*
* This routine is a sample device handler for
*	SER:	The serial port
*	PAR:	The parallel port
*	PRT:	The installed printer
*	PRT:RAW	The installed printer without translating LF to CR
*

* BCPL driver Entry conditions:
*	D1 - DosPacket - immediately converted to a real pointer
*	A0 - 0
*	A1 - Pointer to our global data area
*	A2 - Pointer to BCPL subroutine library
*	A3 - <Unassigned>
*	A4 - Pointer to Base of out code
*	A5 - Pointer to Subroutine transfer vector
*	A6 - Pointer to Code termination vector
*

* Upon initialization, D1 contains a BPTR to a DosPacket with the info:
*	arg1 = BPTR to BCPL string of device name
*		This is should be one of PRT: PRT:RAW SER: PAR:
*	arg2 = Extra info from the DevList entry.
*		0 = SER:	1 = PAR:	2 = PRT:
*	arg3 = BPTR to the DevList entry
*
	LSL.L	#2,D1		*make pointer to DosPacket real

*	Initialize our global structure
*	MOVE.L	A1,$CA
*	MOVE.L	#MARKER1_DATA,MARKER1(A1)	*let us see it in memory
*	MOVE.L	#MARKER2_DATA,MARKER2(A1)
*	MOVE.L	A2,EXECLIB(A1)
*	MOVE.L	A4,OURCODE(A1)
*	MOVE.L	A5,DOSCALL(A1)
*	MOVE.L	A6,DOSRET(A1)
	CLR.L	read_pkt(A1)	*No Read request pending
	CLR.L	write_pkt(A1)	*No write request pending
	CLR.B	openin(A1)	*device not opened for input
	CLR.B	openout(A1)	*device not opened for output
	MOVE.L	dp_Arg3(A0,D1.L),D3
	LSL.L	#2,D3		*make it a real pointer
	MOVE.L	D3,node(A1)	*to our DevList entry

	MOVEQ.L	#inpkt_buf,D3	*locate our default input packet
	ADD.L	A1,D3
	MOVE.L	#act_queueread,dp_Type(A0,D3.L)	*and set it as a read packet
	LSR.L	#2,D3
	MOVE.L	D3,inpkt(A1)

	MOVEQ.L	#outpkt_buf,D4	*locate our default output packet
	ADD.L	A1,D4
	MOVE.L	#act_queuewrite,dp_Type(A0,D4.L) *and set as write packet
	LSR.L	#2,D4
	MOVE.L	D4,outpkt(A1)

	MOVEQ.L	#IOB_buf,D5	*locate our input IO request packet
	ADD.L	A1,D5
	LSR.L	#2,D5
	MOVE.L	D5,IOB(A1)

	MOVE.L	#IOBO_buf,D6	*locate our output IO request packet
	ADD.L	A1,D6
	LSR.L	#2,D6
	MOVE.L	D6,IOBO(A1)

	MOVEQ.L	#IOB_size-1,D5	*Empty our input IOB
	LEA	IOB_buf(A1),A3
CLRIOB	CLR.L	(A3)+
	DBF	D5,CLRIOB

***	Open the device
***	We have to figure out what device we will be opening
	MOVEQ.L	#0,D4		* Flags on the open
	MOVE.L	D4,D3		* Unit number

	TST.L	dp_Arg2(A0,D1.L)	* is it a open on SER:
	BEQ	IS_SER
	MOVEQ.L	#1,D2
	CMP.L	dp_Arg2(A0,D1.L),D2	* is it an open on PAR:
	BEQ	IS_PAR

IS_PRT	LEA	prt_name(A4),A3		PRT: uses printer.device
	BRA	PUTNAME

IS_SER	LEA	ser_name(A4),A3		SER: uses serial.device
	BRA	PUTNAME

IS_PAR	LEA	par_name(A4),A3		PAR: uses parallel.device
PUTNAME	EQU	*
	MOVE.L	A3,D2
	LSR.L	#2,D2		*make the name a BPTR
	MOVE.L	IOB(A1),D1
	BCALL	OpenDevice

	TST.L	D1		*did the open succeed
	BNE	GOTOPEN		*no...

	MOVE.L	#ERROR_OBJECT_IN_USE,D3	*say it is in use
	MOVEQ.L	#FALSE,D2
	MOVE.L	(A1),D1
	BCALL	returnpkt
	BEXIT

GOTOPEN	EQU	*

	MOVEQ.L	#IOB_size-1,D5	*get a second IOB for output
	LEA	IOB_buf(A1),A3
	LEA	IOBO_buf(A1),A0	*Use only for copying - restore to 0 later
COPYIOB	MOVE.L	(A3)+,(A0)+
	DBF	D5,COPYIOB
	MOVEQ.L	#0,D5
	MOVE.L	D5,A0

***	Now see if they asked to put the printer into the RAW mode
	MOVE.L	(A1),D1
	LSL.L	#2,D1
	MOVEQ.L	#2,D5
	CMP.L	dp_Arg2(A0,D1.L),D5	* is it a open on PRT:
	BNE	NOTRAW
	MOVE.L	dp_Arg1(A0,D1.L),D3
	LSL.L	#2,D3
	CMP.L	#RAW_NAME,4(A0,D3.L)	*is the secondpart 'RAW\0'?
	BNE	NOTRAW

***	Build the IOB for outputing the initialization string
	LEA	prt_init_str(A4),A3		*point to the output string
	MOVE.L	A3,D3
	MOVE.L	IOBO(A1),D1
	ASL.L	#2,D1
	MOVE.L	D1,A3
	MOVE.W	#CMD_WRITE,D2
	MOVE.L	D2,IO_COMMAND(A3)	;$1C
	MOVE.L	D3,IO_DATA(A3)		;$28
	MOVEQ.L	#-1,D4
	MOVE.L	D4,IO_LENGTH(A3)	;$24

	MOVE.L	IOBO(A1),D1	*DOIO(IOBO) to output the init string
	BCALL	DoIO

NOTRAW	EQU	*

***	set taskid field to our task so everyone comes to us for the device
	BCALL	taskid
	MOVE.L	node(A1),D2
	MOVE.L	D1,dl_Task(A0,D2.L)

***	We are now setup, return our initilization packet so we can
***	Let the system start sending us IO requests
	MOVEQ.L	#TRUE,D2
	MOVE.L	(A1),D1		IO_parm_pkt
	BCALL	returnpkt

***	Now that all the initialization is done, here is where we do all
***	the work of processing the requests.  Of importance to note is that
***	although we have opened the device, the real request to open the
***	device comes (usually) as the first message to our task.
***
***	Events come to us in the form of a DosMessage with the type of
***	event in the type field of the packet
***	We only need to handle a few of these events and can just send
***	the rest back as invalid requests
***
***	The ones we handle are:
***	'R'	- A request to read from the device
***	'W'	- A request to write to a device
***	1001	- A read WE posted to the device completed
***	1002	- A write WE posted to the device completed
***	1005	- The user wishes to open the device we handle for input
***	1006	- The user wishes to open the device we handle for output
***	1007	- The uses wishes to terminate an open on our device
***
***	Register usage:
***	D1 - BCPL pointer to event message packet
***	D2 - real pointer to event message packet - until changed
***
***	Wait for the next message
LOOP	EQU	*
	BCALL	taskwait
	MOVE.L	D1,D2
	LSL.L	#2,D2

***	Now see what type of message it was
	MOVE.L	dp_Type(A0,D2.L),D3

	MOVEQ.L	#'R',D4
	CMP.L	D4,D3		*read
	BEQ	do_R

	MOVEQ.L	#'W',D4
	CMP.L	D4,D3		*write
	BEQ	do_W

	CMP.L	#act_queueread,D3	*act.read
	BEQ	do_read

	CMP.L	#act_queuewrite,D3	*act.write
	BEQ	do_writ

	CMP.L	#act_findinput,D3	*find input
	BEQ	do_old

	CMP.L	#act_findoutput,D3	*find output
	BEQ	do_new

	CMP.L	#act_end,D3	*end
	BEQ	do_end

	BRA	do_dflt

*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
***
***	Open up our device for input
***
*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
do_old	TST.B	openin(A1)	*make sure it isn't already open for input
	BNE	IN_USE

	ST	openin(A1)	*Say we have the device for input
	MOVE.L	#MODE_OLDFILE,D3
	BRA	SET_SCB

*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
***
***	Open up our device for output
***
*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
do_new	TST.B	openout(A1)
	BNE	IN_USE

	ST	openout(A1)	*Say we have the device for output
	MOVE.L	#MODE_NEWFILE,D3

SET_SCB	MOVE.L	dp_Arg1(A0,D2.L),D4
	LSL.L	#2,D4
	MOVE.L	D3,fh_Arg1(A0,D4.L)	*and set the access mode
	MOVEQ.L	#TRUE,D5
	MOVE.L	D5,fh_Interactive(A0,D4.L)	* Mark file as interactive
	BRA	OK_RET

*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
***
***	Close the device for input/output
***
*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
do_end	CMP.L	#MODE_OLDFILE,dp_Arg1(A0,D2.L)
	BNE	closout
	CLR.B	openin(A1)	*device no longer needed for input
	BRA	doclose
closout	CLR.B	openout(A1)	*device no longer needed for output
doclose	EQU	*

***	If this closed both files then it is time to shut down
	TST.B	openin(A1)
	BNE	OK_RET
	TST.B	openout(A1)
	BNE	OK_RET
	MOVE.L	node(A1),D3	*find our devicelist entry
	CLR.L	dl_Task(A0,D3.L) *remove our process id from the entry
	BRA	OK_RET

*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
***
***	Here we have a read request we posted returning to us
***
*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
do_read	MOVE.L	D1,inpkt(A1)	*restore our packet for posting reads

	MOVE.L	read_pkt(A1),D1	*the message to return to the user
	LEA	IOB_buf(A1),A3	*the IO request that returned
	BRA	CHK_RET

*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
***
***	Here we have a write request we posted returning to us
***
*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
do_writ	MOVE.L	D1,outpkt(A1)	*restore out packet for posting writes

	MOVE.L	write_pkt(A1),D1	*the message to return to the user
	LEA	IOBO_buf(A1),A3		*the IO request that returned

CHK_RET	MOVEQ.L	#0,D3
	MOVE.B	IO_ERROR(A3),D3		*get the error code if any
	BNE	BAD_RET			*any error?
	MOVE.L	IO_ACTUAL(A3),D2	*return number of bytes read
	BRA	RET_PKT

*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
***
***	Here we have a request from the user to do a read
***
*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
do_R	MOVE.L	D1,read_pkt(A1)
	MOVE.L	inpkt(A1),tpkt(A1)
	CLR.L	inpkt(A1)		*Indicate input in progress

	MOVEQ.L	#CMD_READ,D4
	MOVE.L	IOB(A1),D1

	BRA	QUE_PKT

*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
***
***	Here we have a request from the user to do a write
***
*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
do_W	MOVE.L	D1,write_pkt(A1)
	MOVE.L	outpkt(A1),D3
	CLR.L	outpkt(A1)	*Indicate an output is in progress

	MOVEQ.L	#CMD_WRITE,D4
	MOVE.L	IOBO(A1),D1

***	To queue an IO request we need to have set up:
***	D1 - BCPL pointer to the desired IOB
***	D2 - The real pointer to the input request message
***	D3 - message packet associated with the request
***	D4 - I/O request type to be queued
QUE_PKT	MOVE.L	D1,D5
	ASL.L	#2,D5
	MOVE.L	D5,A3

	MOVE.W	D4,IO_COMMAND(A3)		;$1C
	MOVE.L	dp_Arg2(A0,D2.L),IO_DATA(A3)	;$28
	MOVE.L	dp_Arg3(A0,D2.L),IO_LENGTH(A3)	;$24
	CLR.L	IO_OFFSET(A3)			;$28

	MOVE.L	D3,D2
	BCALL	SendIO
	BRA	LOOP

*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
***
***	Here we handle all other requests we do not recognize
***
*** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
do_dflt	TST.B	openin(A1)
	BNE	BAD_ACT
	TST.B	openout(A1)
	BNE	BAD_ACT
	MOVE.L	node(A1),D1	*keep them from bothering us anymore
	CLR.L	dl_Task(A0,D1.L)
BAD_ACT	EQU	*
	MOVE.L	#ERROR_ACTION_NOT_KNOWN,D3
	BRA	BAD_RET

***
***	Here we return the message to the user
***
IN_USE	MOVE.L	#ERROR_OBJECT_IN_USE,D3	*the device is in use
BAD_RET	MOVEQ.L	#FALSE,D2	*Operation had failed
	BRA	RET_PKT
OK_RET	MOVEQ.L	#TRUE,D2	*Operation has succeeded
RET_PKT	BCALL	returnpkt	*D1 already has the packet address in it

LOOPIT	TST.B	openin(A1)	*is there anyone open on us?
	BNE	LOOP
	TST.B	openout(A1)
	BNE	LOOP
	TST.L	outpkt(A1)	*or an output pending?
	BEQ	LOOP
	TST.L	inpkt(A1)	*or an input pending?
	BEQ	LOOP

***
***	Our purpose in life is fullfilled, close the device
***	and quietly fade off into the sunset
***
	MOVE.L	IOB(A1),D1
	BCALL	CloseDevice
	BEXIT

		CNOP	0,4
ser_name	EQU	*-EntryPt
		DC.B	14,'serial.device',0

		CNOP	0,4
par_name	EQU	*-EntryPt
		DC.B	16,'parallel.device',0

		CNOP	0,4
prt_name	EQU	*-EntryPt
		DC.B	15,'printer.device',0

		CNOP	0,4
prt_init_str	EQU	*-EntryPt
		DC.B	$1B,'[20l',0
***
***	Trailer stuff to make this look like a BCPL module
***
		CNOP	0,4
		DC.L	0
		DC.L	1
		DC.L	EntryPt-StartModule
		DC.L	(FRAME_SIZE/4)+2+$44
EndModule	EQU	*
		END



