	opt	o+,ow-			;Devpac 2.0 options

; Before we go to the main subject, I'll give some special information about
; macros in Devpac 2.0.
;
; It is possible to pass a size  (byte, word, long) to macros.  This is done
; with \0 (the SIZE parameter).  If ".b" or ".w" or ".l" is added just after
; the macro name in the macro call then \0 will result in "b" or "w" or "l".
; When no size is given \0 will default to "w".
;
; If you want  to pass a  parameter  that  contains spaces  then enclose the
; parameter in the macro call between these brackets: <>.
;
; Now we go to the main event...
;
;---------------------------------------------------------------------------
;
; Dr. Maybe presents...      >>>> GETDRIVE routine <<<<
;
; I would like to talk about a common problem in programs that write to disk
; at low level (i.e. at bitmap  level).  For  example  a  simple fast-format
; (initialize) function  or installing  an intro or picture in the first few
; tracks of a disk.
;
; What happens? Mostly the bitmap structure of the disk is  changed. In case
; of initializing  a new Root and Bitmap block are written, and in case of a
; bootintro the Bitmap block is changed in such a way that a  few tracks are
; allocated for  the intro.  In both  cases the  information on the disk has
; changed.
;
; But AmigaDOS  must be  informed about  this. Otherwise  AmigaDOS will give
; wrong information  about the  disk. After initializing the disk will still
; be full or not  validated. After  a bootintro  is installed  too much free
; disk space  will be showed. The only way to update the disk information is
; to eject and insert it. Another danger is  when other  tasks want  to have
; access to the disk, while your program is writing to the tracks!
;
; I hope  the problem  is clear.  The program must reserve the drive for own
; use, without permitting other tasks to have access  to it,  and afterwards
; the drive  must be  returned to system. Well, that's not so difficult, you
; might think, lets look it up in the books. I sought and sought, but didn't
; find. And I think most other Amiga programmers have the same problem. Take
; for example "Bootleg" or "Bootgirl" or "Bootem". Only the Workbench Format
; command does drive-handling nicely. You even get a "DF1:BUSY" icon.
;
; So,  what's  the  only  thing  left  to  do for an Amiga freak? Right, you
; guessed it, I disassembled the Format command! Here comes the solution.
;
;---------------------------------------------------------------------------
;
; First find the task that's responsible  for giving  other tasks  access to
; the drive. This is done with the following function from dos.library:
;
;    Process_MessagePort = DeviceProc( name )
;        d0                             d1
;
; Here  d1  points  to  the  name  of  the drive (e.g. 'DF1:',0), which is a
; NULL-terminated string. The returned value in d0 is the MessagePort of the
; DOS process  that's controlling  the drive.  A DOS process is just an exec
; task added with a MessagePort (so  we  can  talk  with  it)  and  some DOS
; information (like  CLI, Filesystem,  etc). All programs loaded from CLI or
; Workbench    are    DOS    processes.     Lets    take     a    look    at
; "include/libraries/dosextens.i": 
;
; STRUCTURE Process,0
;    STRUCT  pr_Task,TC_SIZE     * APTR to exec task
;    STRUCT  pr_MsgPort,MP_SIZE  * This is BPTR address from DOS functions
;    WORD    pr_Pad              * Remaining variables on 4 byte boundaries
;    BPTR    pr_SegList          * Array of seg lists used by this process
;    (etc)
;---------------------------------------------------------------------------
;
; Now we  send a StandardPacket to the process. A standard packet is just an
; exec message added with  some  extras,  called  DOS  packet.  These extras
; consist of  our ReplyPort (to get messages back) and information about the
; command we would like  to  give  to  the  process.  Lets  take  a  look at
; "include/libraries/dosextens.i":
;
; STRUCTURE StandardPacket,0
;   STRUCT sp_Msg,MN_SIZE       * message
;   STRUCT sp_Pkt,dp_SIZEOF     * DOS packet
;   LABEL  sp_SIZEOF
;
; STRUCTURE DosPacket,0
;   APTR   dp_Link         * pointer to EXEC message
;   APTR   dp_Port         * pointer to Reply port for the packet
;                          * Must be filled in each send.
;   LONG   dp_Type         * See ACTION_... below and
;                          * 'R' means Read, 'W' means Write to the file system
;   LONG   dp_Res1         * For file system calls this is the result
;                          * that would have been returned by the
;                          * function, e.g. Write ('W') returns actual
;                          * length written
;   LONG   dp_Res2         * For file system calls this is what would
;                          * have been returned by IoErr()
;   LONG   dp_Arg1
;   (etc)
;
; ACTION_INHIBIT          EQU     31
;   (etc)
;
; The most important element here is  dp_Type. This  element is  filled with
; the command we want to give: ACTION_INHIBIT. For drive reservation we make
; dp_Arg1 = 1 and to return the drive to system we make dp_Arg1 =  0. We can
; do many  things with  packets. Look at the complete include file yourself!
; After setting up the packet properly, we send it to the process  port with
; the  exec  function  PutMsg.  Then  we  GetMsg from drive process back and
; Remove the packet and check error in dp_Res1.
;
;---------------------------------------------------------------------------
;
; Of course you did't understand anything  of this  bulls..t mambo  jambo (I
; kneuw that!),  but it works perfectly. Even the "DF0:BUSY" icon appears in
; Workbench!! For full details look at the GetDrive routine below.
;
; GOOD LUCK!
;
; Dr. Maybe
; Postbus 121
; 6850 AD Huissen
; Holland
;
;---------------------------------------------------------------------------

	incdir	"ram:include/"		;your include directory
	include	"exec/exec_lib.i"
	include	"exec/memory.i"
	include	"exec/ports.i"
	include	"libraries/dos_lib.i"
	include	"libraries/dosextens.i"


CALL	MACRO				;library function call
	jsr	_LVO\1(a6)
	ENDM


	SECTION	main,CODE

;**** Open DOS ****
	lea	_DOSName(pc),a1
	moveq	#0,d0
	CALLEXEC OpenLibrary
	move.l	d0,_DOSBase
	beq	quit

;**** Open RAW Window, so I can talk to you. ****
	lea	rawwindowname(pc),a0
	move.l	a0,d1
	move.l	#MODE_OLDFILE,d2
	CALLDOS	Open
	move.l	d0,rawwindow
	beq	quit

;**** Now get drive for own use. ****
	lea	drivename(pc),a0
	moveq	#1,d0
	bsr	GetDrive
	beq.s	ok

;**** check & display error, and quit ****
	lea	not_mounted(pc),a0
	cmp.b	#218,d0
	beq.s	prt
	lea	doserror(pc),a0
	cmp.b	#100,d0
	beq.s	prt
	lea	drive_in_use(pc),a0
prt
	bsr	print
	bsr	wait_key
	bra.s	quit

ok
;**** drive disabled successfully ****
	lea	drive_disabled(pc),a0
	bsr	print
	bsr	wait_key

;**** return drive to system
	lea	drivename(pc),a0
	moveq	#0,d0
	bsr	GetDrive

quit
;**** cleanup & exit ****
	move.l	rawwindow(pc),d1	;close raw window
	beq.s	.q1
	CALLDOS	Close
.q1
	move.l	_DOSBase(pc),d0		;close DOS
	beq.s	.q2
	move.l	d0,a1
	CALLEXEC CloseLibrary
.q2
	moveq	#0,d0
	rts

print
;**** output text in a0 to rawwindow, and wait ****
	move.l	rawwindow(pc),d1
	move.l	a0,d2
	moveq	#-1,d3
.loop
	addq.l	#1,d3
	tst.b	(a0)+
	bne.s	.loop
	CALLDOS	Write
	rts

wait_key
;**** wait for key in raw window *****
	move.l	rawwindow(pc),d1
	lea	press_key(pc),a0
	bsr	print
	move.l	rawwindow(pc),d1
	lea	buffer(pc),a0
	move.l	a0,d2
	moveq	#1,d3
	CALLDOS	Read
	rts



GetDrive
;***************************************************************************
;                                                                          *
; Drive disable/enable, (C) Dr. Maybe, Huissen 1990                        *
; This routine disables a drive from being used by AmigaDOS, for example   *
; if you want to format a disk, or install a bootintro on it.              *
; In WorkBench this will result in a "BUSY" icon.                          *
; Afterwards drive must be returned to system.                             *
;                                                                          *
; usage:                                                                   *
;       error = GetDrive( drive_name, action )                             *
;        d0                  a0         d0                                 *
;                                                                          *
;       drive_name = NULL-terminated string to drive name (e.g. 'DF1:',0)  *
;       action     = 1 to get drive for own use                            *
;                    0 to return drive to system                           *
;                                                                          *
;       error      = 0 if OK                                               *
;                    103 if not enough memory                              *
;                    218 if drive not mounted                              *
;                    100 if dos error                                      *
;                    126 if disable failed                                 *
;                                                                          *
;***************************************************************************

	move.l	d0,d2			;d2 = action
	move.l	a0,a3			;a3 = drive name


;===========================================================================
; Allocate memory for StandardPacket structure                             #
;===========================================================================

	moveq	#sp_SIZEOF,d0
	move.l	#MEMF_CLEAR+MEMF_PUBLIC,d1
	CALLEXEC AllocMem
	bne.s	.cnt3

	moveq	#103,d0
	rts
.cnt3
	move.l	d0,a2			;a2 = StandardPacket


;===========================================================================
; Initialize a StandardPacket to send to process that controls the drive   #
;===========================================================================

	lea	sp_Pkt(a2),a0
	move.l	a0,sp_Msg+MN+LN_NAME(a2);message node name = DOS packet
	move.l	a2,sp_Pkt+dp_Link(a2)	;pointer to exec message

	sub.l	a1,a1			;own task (process)
	CALL	FindTask
	move.l	d0,a1
	lea	pr_MsgPort(a1),a0	;own task message port is
	move.l	a0,d4
	move.l	a0,sp_Pkt+dp_Port(a2)	;=> ReplyPort for packet

	moveq	#ACTION_INHIBIT,d0	;dospacket type/action (=INHIBIT)
	move.l	d0,sp_Pkt+dp_Type(a2)
					;=========================
	move.l	d2,sp_Pkt+dp_Arg1(a2)	; dospacket Arg          #
					; 1 = disable drive task #
					; 0 = enable drive task  #
					;=========================


;===========================================================================
; Get MessagePort of process that controls the drive (trackdisk?)          #
;===========================================================================

	move.l	a3,d1			;DrivePort = DeviceProc(drivename)
	CALLDOS	DeviceProc
	bne.s	.cnt1
	move.l	#218,d2
	bra.s	.end
.cnt1


;===========================================================================
; Now send the StandardPacket to the drive process                         #
;===========================================================================

	move.l	d0,a0			;MessagePort of drive task
	move.l	a2,a1			;packet (message)
	CALLEXEC PutMsg


;===========================================================================
; Check result & exit                                                      #
;===========================================================================

	moveq	#0,d2			;error code

;WAIT UNTIL WE GET A MESSAGE BACK
	move.l	d4,a0			;our task's reply port
	CALL	WaitPort
	cmp.l	d0,a2			;is it the right reply message?
	beq.s	.cnt2

	moveq	#100,d2			;dos failure/wrong reply message
.cnt2

;REMOVE PACKET FROM LIST
	move.l	a2,a1			;remove packet from list
	CALLEXEC Remove
	tst.l	sp_Pkt+dp_Res1(a2)	;check result
	bne.s	.end

	moveq	#126,d0

.end
;EXIT WITH ERROR IN D0
	move.l	a2,a1			;free memory for StandardPacket
	moveq	#sp_SIZEOF,d0
	CALLEXEC FreeMem

	move.l	d2,d0
	rts

;===========================================================================

_DOSBase	dc.l	0
rawwindow	dc.l	0
buffer		dc.l	0

_DOSName	dc.b	"dos.library",0
rawwindowname	dc.b	"RAW:120/78/400/100/GetDrive",0
drivename	dc.b	"DF0:",0

not_mounted	dc.b	10," Drive not mounted!",10,10,0
doserror	dc.b	10," DOS Error. Packet reply failure!",10,10,0
drive_in_use	dc.b	10," Can't get drive (in use?)",10,10,0
drive_disabled	dc.b	10," Drive DF0: disabled.",10,10,0
press_key	dc.b	" Press any key to return...",10,10

