
; Install.s
; It istalls two routines at the DoIO and SendIO vectors.
; To be Assembled with SAS/C 6.50 Assembler and linked with slink
; to produce SofTrack:  1> asm Install.s 
; Written by Kamran Karimi.
 

 INCLUDE "exec/memory.i"
 INCLUDE "exec/ports.i"
 INCLUDE "exec/io.i"
 INCLUDE "devices/trackdisk.i"
 INCLUDE "dos/dos.i"

ExecBase equ 4

 ;EXEC library 
PutMsg      equ -366
Allocmem    equ -198
FreeMem     equ -210
Forbid      equ -132
Permit      equ -138
SetFunction equ -420

; DoIO and SendIO offsets point to the actual jump address, not the 'jmp'
; instruction before them. the jmp for DoIO and SendIO are at offset -456 and -462
; respectvely. As you can see, the 'jmp' opcode has two bytes.
DoIO     equ -454
SendIO   equ -460

; the main proram will prepare the message port and the routines in this
; file will send information to it
 XREF _Port

; these variables are initialized here but used in the main program
 XREF _ReservedDoIO
 XREF _ReservedSendIO
 XREF _SaveDoIO
 XREF _SaveSendIO
 XREF _SizeOfIO


 SECTION inst,CODE

 XDEF _Install

_Install:

; subroutines in Amiga can use registers d0, d1, a0 and a1 as scratch.
; all other registers should remain intact, so we save a6, as we will
; over write it here

 move.l  a6,-(a7)

; initialize message port parameters before they are copied 

 lea     Message,a1
 move.b  #NT_MESSAGE,LN_TYPE(a1)      ;message type
 move.w  #MN_SIZE+8,MN_LENGTH(a1)      ;message length

; save the original vector addresses.

 move.l  ExecBase,a6
 move.l  DoIO(a6),_SaveDoIO
 move.l  SendIO(a6),_SaveSendIO

; the two routines for DoIO and SendIO are identical, so we copy the 
; code provided here in two newly allocated places in memory.
; each one will have its own structures and the ability to send messages
; to the main program's port.
;
; NOTE: Amiga does not support run-time relocation of code. 
; to be able to move a piece of code after it is loaded in memory,
; all the addressings in it should be PC relative.

 move.l  ExecBase,a6
 move.l  _Port,Port 
 move.l  #EndeIO-BeginIO,d0
 move.l  d0,_SizeOfIO
 move.l  #MEMF_PUBLIC,d1
 jsr     Allocmem(a6) ; allocate memory for DoIO
 move.l  d0,_ReservedDoIO
 beq     End_Main ; no mem?
 move.l  _SizeOfIO,d0
 move.l  #MEMF_PUBLIC,d1
 jsr     Allocmem(a6) ; another for SendIO
 move.l  d0,_ReservedSendIO
 bne     MemAllocated
 move.l  _ReservedDoIO,a1 ; if failed, free the DoIO memory
 move.l  _SizeOfIO,d0
 jsr     FreeMem(a6)
 bra     End_Main

; memory allocated, now copy the code provided under the BeginIO label
; to these places.

MemAllocated: 

; we need the original vector address, so that we can activate it after 
; the information have been sent to the main program

 move.l  DoIO(a6),_SaveIO  
 move.l  #BeginIO,a0 ; source
 move.l  _ReservedDoIO,a1 ;destination
 move.l  _SizeOfIO,d0 ; size in bytes
 divu    #2,d0 ; a word (2 bytes) at a time
 subi.l  #1,d0 ; 'dbra' stops when the counter in d0 reaches -1!
lop1:
 move.w  (a0)+,(a1)+ ; do the copy
 dbra    d0,lop1 ; 'dbra' done?

 move.l  ExecBase,a1
 move.l  #DoIO-2,a0
 move.l  _ReservedDoIO,d0
 jsr     SetFunction(a6) ; DoIO vector is changed
 
; the above operations are repeated for SendIO

 move.l  SendIO(a6),_SaveIO
 move.l  #BeginIO,a0
 move.l  _ReservedSendIO,a1
 move.l  _SizeOfIO,d0
 divu    #2,d0
 subi.l  #1,d0
lop2:
 move.w  (a0)+,(a1)+
 dbra    d0,lop2

 move.l  ExecBase,a1
 move.l  #SendIO-2,a0
 move.l  _ReservedSendIO,d0
 jsr     SetFunction(a6)

 moveq   #RETURN_OK,d0 ; we succeded
 bra     Payan
End_Main:
 move.l  #RETURN_FAIL,d0 ; we failed
Payan:   
 move.l (a7)+,a6
 rts


; this is the common routine for getting disk operation information.
; all addresses are relative to program counter, to enable it to be moved
; around in memory

BeginIO:

; save the original register contents. we should give them intact to
; the original DoIO ro SendIO routine.
;
; the address of a ioRequest is in a1. this contains all the information
; about the requested action.

 movem.l d0-d4/a0-a4/a6,-(a7)

; first we see if the request is for 'trackdisk.device'

 move.l  IO_DEVICE(a1),a2 ; pointer to device structure is in a2 
 move.l  LN_NAME(a2),a3
 move.l  #15,d0
 lea     TrackName(pc),a4
lop22:
 cmpm.b  (a3)+,(a4)+
 bne     Endit ; not for 'trackdisk.device'. just call the original routine 
 dbra    d0,lop22

; prepare the message structure to be filled with information

 lea     Message(pc),a2
 move.l  IO_UNIT(a1),a4 ; pointer to a Unit structure is in a4

; this offset ($41) is not documented! (see SofTrack.doc)
; it seems that this code breaks under 2.0 and above! (i.e. the information
; for all drives is shown in the df1 space.

 move.b  $41(a4),d0

; is it for DF0?

 btst    #3,d0
 bne     df1
 move.b  #0,20(a2)
 bra     Cont
df1:
 btst    #4,d0 ;maybe df1?
 bne     df2
 move.b  #1,20(a2)
 bra     Cont
df2:
 btst    #5,d0 ;or df2 ?
 bne     df3
 move.b  #2,20(a2)
 bra     Cont
df3:
 btst    #6,d0 ; or df3 ?
 bne     Endit ; none of them? just call the original routine.
 move.b  #3,20(a2)
Cont:
 move.w  IO_COMMAND(a1),d0         ;d0 has command
 bclr    #TDB_EXTCOM,d0 ; being an extended command is not interesting to us

; now find out the command (read, write, format... )

 cmpi.w  #CMD_READ,d0
 bne     NotR
 move.b  #'R',26(a2)
 bra     TrackChange
NotR:
 cmpi.w  #TD_SEEK,d0
 bne     NotS
 move.b  #'S',26(a2)
 bra     TrackChange
NotS:
 cmpi.w  #TD_RAWREAD,d0
 bne     NotRawR
 move.b  #'R',26(a2)
 bra     TrackChange
NotRawR:
 cmpi.w  #CMD_WRITE,d0
 bne     NotW
 move.b  #'W',26(a2)
 bra     TrackChange
NotW:
 cmpi.w  #TD_FORMAT,d0
 bne     NotF
 move.b  #'F',26(a2)
 bra     TrackChange
NotF:
 cmpi.w  #TD_RAWWRITE,d0
 bne     Endit
 move.b  #'W',26(a2)

TrackChange:
 moveq   #0,d4  
 move.l  #16,d2 ; used for shifts. the result of divu are 16 bit.
 move.l  44(a1),d1  ;sector offset

;11264 = 512 (bytes per sector) * 11 (sectors per track) * 2 (sides per disk)

 move.l  #11264,d3
 divu    d3,d1     ;track
 andi.l  #$0000ffff,d1 ; we need only the quotient 

; now turn the track number into ASCII, so that it can be displayed
; to the user

 divu    #100,d1
 addi.b  #48,d1
 cmp.b   #'0',d1
 bne     NZ1
 move.b  #' ',d1 ; don't show 0
 moveq   #1,d4
NZ1:
 move.b  d1,22(a2)
 lsr.l   d2,d1 ; now the remainder
 divu    #10,d1
 addi.b  #48,d1
 tst     d4
 beq     NZ2
 cmp.b   #'0',d1
 bne     NZ2
 move.b  #' ',d1
NZ2:
 move.b  d1,23(a2)
 lsr.l   d2,d1
 addi.b  #48,d1
 move.b  d1,24(a2)

 move.l  ExecBase,a6
 move.l  Port(pc),a0
 lea     Message(pc),a1
 jsr     PutMsg(a6)

Endit:

; restore the original register contents.

 movem.l (a7)+,d0-d4/a0-a4/a6

; push the original routine address on the stack. now an 'rts' will
; transfer control to it.

 move.l  _SaveIO(pc),-(a7)
 rts

_SaveIO:    dc.l 0

Message:    ds.b 20 ;<- this is the main message structure
            dc.w 0  ; <- the unit obeying the command is put here
            dc.b "000  ",0 ; <- information to be displayed is put here

; message port address 

Port:       dc.l 0

TrackName: dc.b "trackdisk.device",0,0
EndeIO:

 END
