Description of the hard disk driver functions of MagiC 3.00 onwards
###################################################################

Andreas Kromke
Hannover,  8.2.95
English translation: Peter West, May 99


Driver installation
===================

MagiC contains the Dcntl function KER_DOSLIMITS for hard disk drivers.
This returns information about the capacity of the internal FAT file 
system driver, i.e. how many clusters etc. can be handled. For further 
details see MGX_DOS.TXT and MGX_XFS.H (in the DOS and this FILESYS folder 
respectively).

MagiC requires an AHDI-compatible hard disk driver. After the start of 
the hard disk driver MagiC has to restructure its internal buffer list,
and for this it needs the information about how large the sector buffers 
installed by the driver are. If this information is wrong then it leads 
to a serious crash. MagiC verifies the pointer pun_ptr. If this is NULL, 
then it will be assumed that no buffers were installed (MagiC then uses
its 2+2 buffers each with 512 bytes), else pun_msectsize (as UWORD) is 
used to establish the size of the new buffers. If these are larger than 
those installed by MagiC earlier then the old buffers are released, i.e.
converted to internal DOS memory. Following this the new buffers are 
installed and _bufl[0] and _bufl[1] again set to NULL.


I Concepts
==========

The complete DOS including the accesses to DOS file systems is reenttrant 
and runs in the background. Thus it is possible to edit files on drive A: 
for instance without any sign of slowing down the computer during any 
accesses to the floppy disk. A prerequisite for this is floppy and hard 
disk routines that operate in the background.

The extracts from the BIOS included in this file serve only for information 
about how the whole thing works. These routines may change at any time in 
the future.

So that the hard disk driver does not have to make this function 
available, the BIOS of MagiC contains all the required functions.

For this the structure:

typedef struct
     {
     long    config_status;
     DOSVARS *dosvars;
     AESVARS *aesvars;
     } MAGX_COOKIE;

has been extended to:

typedef struct
     {
     long    config_status;
     DOSVARS *dosvars;
     AESVARS *aesvars;
     void    *reserved;
     HDFUNCS *hdf;
     } MAGX_COOKIE;

typedef struct
     {
     long      dma_begin ( void );
     long      dma_end   ( void );
     long      dma_wait  ( d0 = long ticks_200hz );
     long      ncr_begin ( void );
     long      ncr_end   ( void );
     long      ncr_wait  ( d0 = long ticks_200hz );
     } HDFUNCS;

Directly before the structure (2 bytes before) there is a WORD that 
specifies the length of the table, in this case 6. All 6 pointers lie in 
the system variable area and may be altered if necessary. The internal
functions for the floppy drive also jump over the 6 pointers. The pointer 
<hdf> in the cookie may not be altered.

The version number of MagiC, which determines the presence of this 
extended cookie, unfortunately can not be established via AESVARS because 
this pointer to the AES variables is still NULL at boot time. Luckily one 
can also reach this pointer via the TOS system header, and this pointer 
is always valid. At boot time the preemptive multitasking is switched off 
(actually all multitasking is switched off), but in any case the routines 
also work before the installation of the AES, when the wait routines 
execute a "busy waiting" (as they do when switching off preemptive 
multitasking), and the semaphore routines simply do nothing (apart from 
setting and clearing of Flock).

The routines acsi_xxx are simultaneously for ACSI and FDC, i.e. dma_begin
sets Flock, dma_end clears it again.
The routines ncr_xxx are for TT-SCSI.

The functions xxx_begin reserve the relevant semaphores. A return value 0
means "OK", -1 means "Semaphores already belong to me". The routines alter 
no registers apart from d0. There is no timeout, though the semaphores 
are released at the termination of the application.

The functions xxx_end release the semaphores once more, no register is 
altered.

dma_wait waits for an interrupt by the ACSI-DMA controller or the FDC,
ncr_wait is for the SCSI bus of the TT.
Return values of xxx_wait: 0 = OK, -1 = Timeout, -2 = Bus error 
(interrupt from the TT-SCSI).

Semaphores are released at program termination (more exactly: at the 
termination of the application), including those set with the Psemaphore 
DOS call (in contrast to MiNT!!!!).
Psemaphore() only supports Modes 2 and 3. If one sets the semaphore _DMA
with Psemaphore, one must not forget to set Flock after receiving the 
semaphore, and to clear it again after release of the semaphore.

System semaphores:

     _SCR      AES screen (wind_update)
     _DMA      ACSI and FDC
     _NCR      TT-SCSI

#####################################################
####### Extract from the BIOS : ACSI ################
#####################################################


* Interrupt control:
*
* Everything works via input I5 of the ST-MFP, has the interrupt #7
* (activated with bit 7 of ierb)
* Polling via bit 5 of gpip
* 1st aer for bit 5 must be 0, i.e. interrupt is triggered at the 
*     transition from 1 to 0
* 2nd Activate interrupt _mfpint (7) and set vector (Adr. $11c)
*

**********************************************************************
*
* Lock the FDC/ACSI-DMA
* and release it again.
* No register (apart from d0 at dma_end) is altered
*
* For the time that the AES is not yet initialised, evnt_sem can not 
* lock, because act_appl is always NULL.
*

dma_begin:
 movem.l  d1-d2/a0-a2,-(sp)
 lea      dma_sem,a0
 moveq    #0,d1               		; No timeout
 moveq    #SEM_SET,d0
 jsr      evnt_sem
 st       flock
 movem.l  (sp)+,d1-d2/a0-a2
 rts

dma_end:
 movem.l  d0-d2/a0-a2,-(sp)
 lea      dma_sem,a0
 moveq    #SEM_FREE,d0
 jsr      evnt_sem
 clr.w    flock
 movem.l  (sp)+,d0-d2/a0-a2
 rts


**********************************************************************
*
* long wait_ACSI( d0 = long ticks_200hz )
*
* Returns:     0    OK
*             -1    Timeout
*             -2    Bus error
*

wait_ACSI:
 movem.l  d1-d2/a0-a2,-(sp)
 tst.w    pe_slice            		; Preemptive?
 bmi.b    wdma_no_yield       		; No, busy waiting
 move.l   act_appl,d2
 ble.b    wdma_no_yield       		; Current application invalid

* New routine via evnt_IO and MFP-interrupt

 lsr.l    #2,d0               		; AES: 50Hz instead of 200Hz
wdma_neu:
 move     sr,d1
 ori      #$700,sr
 btst     #5,gpip             		; Finished?
 beq.b    wdma_ok2            		; Yes, enable interrupt
; Set up interrupt
 pea      int_mfp7_unsel(pc)
 move.l   d2,imfp7_appl       		; act_appl
 move.l   sp,imfp7_unsel
; Release interrupt
 move.w   d1,sr
; Wait for interrupt
 move.l   sp,a0
;move.w   d0,d0               		; Timeout in 50Hz-ticks
 jsr      evnt_IO
 addq.l   #4,sp
wdma_ende:
 movem.l  (sp)+,d1-d2/a0-a2
 rts

* Old routine with busy waiting via _hz_200

wdma_no_yield:
 add.l    _hz_200,d0
wdma_loop:
 btst     #5,gpip
 beq.b    wdma_ok
 cmp.l    _hz_200,d0
 bcc.b    wdma_loop
wdma_timeout:
 moveq    #-1,d0              		; Timeout
 bra.b    wdma_ende
wdma_ok2:
 move.w   d1,sr
wdma_ok:
 moveq    #0,d0               		; OK
 bra.b    wdma_ende


**********************************************************************
*
* Interrupt routine for MFP, interrupt channel #7 = I/O-port 5
* (DMA/FDC busy)
*
* Return value 0 (OK)
*

int_mfp7:
 tst.l    imfp7_unsel                   ; Interrupt activated?
 beq.b    imfp7_ende                    ; No, continue
 movem.l  d0-d2/a0-a2,-(sp)

 move.l   imfp7_unsel,a0
 clr.l    imfp7_unsel                   ; Deactivate interrupt
 clr.l    (a0)                          ; Mark as arrived

 move.l   imfp7_appl,a0
 jsr      appl_IOcomplete               ; Wake up waiting APP
 movem.l  (sp)+,d0-d2/a0-a2
imfp7_ende:
 move.b   #$7f,isrb                     ; Clear service-bit
 rte


**********************************************************************
*
* void int_mfp7_unsel( a0 = long *unselect, a1 = APPL *ap );
*
* Deactivates the interrupt again, if it has not arrived
* Return value -1 (Timeout)
*

int_mfp7_unsel:
 clr.l    imfp7_unsel                   ; Deactivate interrupt
 moveq    #-1,d0
 move.l   d0,(a0)                       ; Not arrived
 rts



#####################################################
####### Extract from the BIOS : SCSI ################
#####################################################


* Interrupt control:
*
* SCSI-DMA-Bus error: Input I5 of the TT-MFP
* 1st aer for bit 5 must be 0, i.e. interrupt is triggered at the
*     transition of 1 to 0
* Polling via bit 5 of gpip
* Interrupt #7 of the TT-MFP (address $15c)

* SCSI: Input I7 of the TT-MFP
* 1st aer for bit 7 must be 1, i.e. interrupt is triggered at the
*     transition of 0 to 1
* Polling via bit 7 of gpip
* Interrupt #15 of the TT-MFP (address $17c)

* During the transmission, it seems to be necessary to set bit 3 
* (enable process interrupt) in the operating mode register $fff785 of 
* the ncr 5380.

**********************************************************************
*
* Interrupt routine for TT-MFP, interrupt channel #7 = I/O-port 5
* (SCSI-DMA Bus error)
*
* Return value -2
*

int_scsidma:
 tst.l    ncrdma_unsel                  ; Interrupt activated ?
 beq.b    incrdma_ende                  ; No, continue
 movem.l  d0-d2/a0-a2,-(sp)

 moveq    #-2,d0                        ; Arrived (error)
 move.l   ncrdma_unsel,a0
 clr.l    ncrdma_unsel                  ; Deactivate interrupt
 move.l   d0,(a0)                       ; Mark as arrived

 move.l   ncrdma_appl,a0
 jsr      appl_IOcomplete               ; Wake up waiting APP
 movem.l  (sp)+,d0-d2/a0-a2
incrdma_ende:
 move.b   #$7f,isrb+$80                 ; Clear service-bit (TT-MFP)
 rte


**********************************************************************
*
* Interrupt routine for TT-MFP, interrupt channel #15 = I/O-port 7
* (SCSI)
*
* Return value 0
*

int_ncr:
 tst.l    ncrdma_unsel                  ; Interrupt activated ?
 beq.b    incr_ende                     ; No, continue
 movem.l  d0-d2/a0-a2,-(sp)

 move.l   ncrdma_unsel,a0
 clr.l    ncrdma_unsel                  ; Deactivate interrupt
 clr.l    (a0)                          ; Mark as arrived

 move.l   ncrdma_appl,a0
 jsr      appl_IOcomplete               ; Wake up waiting APP
 movem.l  (sp)+,d0-d2/a0-a2
incr_ende:
 move.b   #$7f,isra+$80                 ; Clear service-bit (TT-MFP)
 rte


**********************************************************************
*
* void incrdma_unsel( a0 = long *unselect, a1 = APPL *ap );
*
* Deactivates the interrupt again, if it has not arrived.
* (Return value -1)
*

incrdma_unsel:
 clr.l    ncrdma_unsel                  ; Deactivate interrupt
 moveq    #-1,d0                        ; Timeout
 move.l   d0,(a0)                       ; Not arrived
 rts


**********************************************************************
*
* long wait_NCR( d0 = long ticks_200hz )
*
* Returns:     0    OK
*             -2    Bus error
*             -1    Timeout
*
* No register apart from d0 is altered
*

wait_NCR:
 movem.l  d1-d2/a0-a2,-(sp)
 tst.w    pe_slice            		; Preemptive?
 bmi.b    wncr_no_yield       		; No, busy waiting
 move.l   act_appl,d2
 ble.b    wncr_no_yield       		; Current application invalid

* New routine via evnt_IO and MFP-interrupt

 lsr.l    #2,d0               		; AES: 50Hz instead of 200Hz
wncr_neu:
 move     sr,d1
 ori      #$700,sr
 btst     #5,gpip+$80         		; DMA-bus error ?
 beq.b    wncr_err2           		; Yes, return(-2)
 btst     #7,gpip+$80         		; Finished?
 bne.b    wncr_ok2            		; Yes, enable interrupt, return(0)

; Set up interrupt
 pea      incrdma_unsel(pc)
 move.l   d2,ncrdma_appl      		; act_appl
 move.l   sp,ncrdma_unsel

; Release interrupt
 move.w   d1,sr

; Wait for interrupt
 move.l   sp,a0
;move.w   d0,d0               		; Timeout in 50Hz-ticks
 jsr      evnt_IO
 addq.l   #4,sp
wncr_ende:
 movem.l  (sp)+,d1-d2/a0-a2
 rts

* Old routine with busy waiting via _hz_200

wncr_no_yield:
 add.l    _hz_200,d0
wncr_loop:
 btst     #5,gpip+$80
 beq.b    wncr_err
 btst     #7,gpip+$80
 bne.b    wncr_ok
 cmp.l    _hz_200,d0
 bcc.b    wncr_loop
 moveq    #-1,d0                   	; Timeout
 bra.b    wncr_ende
wncr_ok2:
 move.w   d1,sr
wncr_ok:
 moveq    #0,d0
 bra.b    wncr_ende
wncr_err2:
 move.w   d1,sr
wncr_err:
 moveq    #-2,d0
 bra.b    wncr_ende


**********************************************************************
*
* Lock the NCR-SCSI
*
* No register apart from d0 is altered
*
* and release it again.
*
* No register is altered
*
* For the time that the AES is not yet initialised, evnt_sem can not 
* lock, because act_appl is always NULL.
*

ncr_begin:
 movem.l  d1-d2/a0-a2,-(sp)
 lea      ncr_sem,a0
 moveq    #0,d1               		; No timeout
 moveq    #SEM_SET,d0
 jsr      evnt_sem
 movem.l  (sp)+,d1-d2/a0-a2
 rts

ncr_end:
 movem.l  d0-d2/a0-a2,-(sp)
 lea      ncr_sem,a0
 moveq    #SEM_FREE,d0
 jsr      evnt_sem
 movem.l  (sp)+,d0-d2/a0-a2
 rts

