;
;       ReSizeable RAMDisk device driver
;
;       Copyright (C) 1992-1996, 2005 Marko Kohtala
;       Released under GNU GPL, read the file 'COPYING' for more information
;
;       To compile with TASM, find the appropriate srd???.asm file and:
;               tasm /m2 srd???.asm
;               tlink /t srd???.obj,srd???.sys

[list -]
;=== Push trace listing source: dosstruc.mac
%include "dosstruc.mac" ; NASM included file
;=== Pop trace listing source
%include "lmacros3.mac"
[list -]
%include "amis.mac"
[list +]

%define DR_ATTRIBUTES DHATTR_NOFAT | DHATTR_OPENCLOSE

%if CAPABLE & C_32BITSEC
%xdefine DR_ATTRIBUTES DR_ATTRIBUTES | DHATTR_32BIT
%endif

%if CAPABLE & C_GIOCTL
%xdefine DR_ATTRIBUTES DR_ATTRIBUTES | DHATTR_GIOCTL
%endif

;**************************************************************************
;
;                Device driver start and the structures
;
;**************************************************************************

section D_SEG
                assume ds:d_seg, cs:d_seg

;                org     0
; The following is to be considered as both a device driver header and
; as a starting point for the configuration table. This driver will be
; identified by its segment address and this structure must be at offset
; 0.

                ; Device driver header
drhdr_next      dd      -1              ; Pointer to next device (now last)
drhdr_attr      dw      DR_ATTRIBUTES
drhdr_strategy  dw      j_strategy	; Offset to strategy function
drhdr_commands  dw      j_commands	; Offset to commands function
drhdr_units     db      1               ; Number of units

; The rest has four functions to be considered
;  1) usable as device driver name if this driver is changed
;     into character device on init.
;  2) usable as a label to be returned in media check call
;  3) identifies this device driver as SRDISK driver by always having
;     the letters SRD at offset dr_ID
;  4) identifies the memory used by the 4 char string at offset dr_memory

dr_volume       label byte
dr_ID           db 'SRD'                ; SRDISK signature (3 char)
dr_memory       db MEMSTR               ; Memory type identifier (4 char)
dr_version      db SRD_VERSION          ; Device driver version (4 char)
                db 0                    ; null to end volume label
dr_v_format     db V_FORMAT             ; Configuration format version
dr_conf         dw offset conf          ; Pointer to drive configuration

j_strategy:	fill 3, 90h, jmp strategy
j_commands:	jmp commands

;**************************************************************************
;
;               I/O ROUTINE TO THE RAM DISK
;
; This routine will read a group of sectors inside this part of
; the ram disk. If starting sector is not on this part of the disk,
; return without error with 0 sectors transferred. If not all sectors
; are on this part of the disk, transfer as many as can and report the
; number of sectors transferred.
;
; On entry
;   DS:SI - local configuration structure
;   dIO_bytes      word ptr [bp-6]   - bytes to move
;   dIO_byteoffset (d)word ptr [bp-4]- byte offset of start
;   dIO_buffer     dword ptr [bp+6]  - buffer
;   dIO_sector     dword ptr [bp+10] - sector
;   dIO_count      word ptr [bp+14]  - count
;   dIO_RW         byte ptr [bp+16]  - RW (0 = read, nonzero write)
;
; Preserve
;   ds, si, bp and stack contents
;
; Return
;   - carry clear if success
;   - carry set on error
;
;**************************************************************************

disk_IO         proc far                ; Disk access function

%if METHOD EQ 1
                push si
                mov si,offset XMS_alloc + XMS_cblk
                mov cx,[dIO_bytes]
                mov [si + XMS_sizel],cx
                xor cx,cx                       ; cx is zero here
                mov [si + XMS_sizeh],cx

                les di,[dIO_buffer]
                mov ax,word ptr [dIO_byteoffset]
                mov dx,word ptr [dIO_byteoffset+2]
                mov bx,[XMS_alloc + XMS_handle]

                test byte [dIO_RW],-1                  ; Input/output?
                jnz @@write
                                                ; -- Read
                mov [si + XMS_shandle],bx         ; Source in XMS
                mov [si + XMS_soffl],ax
                mov [si + XMS_soffh],dx
                mov [si + XMS_dhandle],cx         ; Destination in main memory
                mov [si + XMS_doffl],di
                mov [si + XMS_doffh],es
                jmp @@move
@@write:                                          ; -- Write
                mov [si + XMS_shandle],cx         ; Destination in main memory
                mov [si + XMS_soffl],di
                mov [si + XMS_soffh],es
                mov [si + XMS_dhandle],bx         ; Source in XMS
                mov [si + XMS_doffl],ax
                mov [si + XMS_doffh],dx
@@move:
                mov ah,0Bh                      ; Move XMS block
                call far [XMS_alloc + XMS_entry]        ; ds:si -> control block
                pop si
                debug 'X',ax
                shr ax,1
                cmc                             ; Carry set if err
                ret
%endif

%if METHOD EQ 2
                push si
                mov si,offset EMS_alloc + EMS_cblk
                mov cx,[dIO_bytes]
                mov [si + EMS_sizel],cx
                xor cx,cx                       ; cx is zero here
                mov [si + EMS_sizeh],cx

                les di,[dIO_buffer]
                mov ax,word ptr [dIO_byteoffset]
                mov dx,word ptr [dIO_byteoffset+2]
                mov bx,16*1024
                div bx                          ; ax=page, dx=offset
                mov bx,[EMS_alloc + EMS_handle]

                test byte [dIO_RW],-1                  ; Input/output?
                jnz @@write
                                                ; -- Read
                mov byte [si + EMS_stype],1            ; Source in EMS
                mov [si + EMS_shandle],bx
                mov [si + EMS_soff],dx
                mov [si + EMS_spage],ax
                mov [si + EMS_dtype],cl           ; Destination in main memory
                mov [si + EMS_dhandle],cx
                mov [si + EMS_doff],di
                mov [si + EMS_dpage],es
                jmp @@move
@@write:                                          ; -- Write
                mov [si + EMS_stype],cl           ; Destination in main memory
                mov [si + EMS_shandle],cx
                mov [si + EMS_soff],di
                mov [si + EMS_spage],es
                mov byte [si + EMS_dtype],1            ; Source in EMS
                mov [si + EMS_dhandle],bx
                mov [si + EMS_doff],dx
                mov [si + EMS_dpage],ax
@@move:
                mov ax,EMS_MOVE_MEM_REGION * 100h ; Move EMS region
                int 67h                         ; ds:si -> control block
                pop si
                debug 'X',ax
                cmp ah,1                        ; Carry set if err
                cmc
                ret
%endif

%if METHOD EQ 3
                push ds
                push si

                mov dx,[EMS_alloc + EMS_handle]
                mov ah,EMS_SAVE_PMAP
                int 67h
                or ah,ah
                jnz @@fail

                mov ax,word ptr [dIO_buffer+2]
                mov si,word ptr [dIO_buffer]
                mov bx,[EMS_alloc + EMS_frame]
                cmp byte [dIO_RW],0
                jnz @@set_segments
                  xchg ax,bx
                  mov di,si

@@set_segments:   mov ds,ax
                mov es,bx
                mov bx,-1
                assume ds:nothing

                ; es:di - target address on disk or memory
                ; ds:si - source address on memory or disk
                ; bx - last sectors page

@@next_page:      mov dx,word ptr [dIO_byteoffset+2]
                mov ax,word ptr [dIO_byteoffset]
                mov cx,16*1024
                div cx                          ; ax = page, dx = offset
                cmp ax,bx
                jz @@move_sector

                mov bx,ax                       ; Logical page
                cmp byte [dIO_RW],0
                jnz @@write
                mov si,dx                       ; read, si = offset into page
                jmp @@map_page
@@write:          mov di,dx                       ; @@write, di = offset into page
@@map_page:       xor al,al                       ; Phys page 0
                mov dx,[cs:EMS_alloc + EMS_handle]
                mov ah,EMS_MAP_HANDLE_PAGES
                int 67h
                or ah,ah
                jnz @@fail

@@move_sector:    mov ax,[cs:conf + c_BPB_bps]
                mov cx,ax
                shr cx,1
                rep movsw
                sub [dIO_bytes],ax
                jbe @@done

                add word ptr [dIO_byteoffset],ax
                adc word [dIO_byteoffset+2],0
                jmp @@next_page

@@fail:           stc
                jmp @@finnish

@@done:           clc
@@finnish:
                pushf
                mov dx,[cs:EMS_alloc + EMS_handle]
                mov ah,EMS_RESTORE_PMAP
                int 67h
;                cmp ah,1 ; Set carry if nonzero
;                cmc
                popf
                pop si
                pop ds
                ret

                assume ds:d_seg
%endif

disk_IO         endp

%ifn CAPABLE & C_NOALLOC

;**************************************************************************
;
;               EXTERNAL MEMORY ALLOCATION ROUTINE
;
; This routine is only used if CAPABLE has C_NOALLOC bit clear.
;
; Allocate requested amount of memory. If memory is available in chunks,
; the amount can be rounded up. If not enough memory available, allocate
; as much as possible or just report the amount that was previously
; allocated. It is expected that at least as much memory can be allocated
; as there previously was. Reallocation should not destroy memory
; contents - it is essential to be able to resize a disk without losing
; the contents.
;
; On entry
;   DWORD [sp+4] - Kbytes to allocate
;
; Preserve
;   es, ds
;   si, di
;
; Return dx:ax = Kbytes allocated
;
;**************************************************************************

malloc          proc far              ; Memory allocation function

 %if 0
                arg kbytes:dword
 %endif
	lframe far
	lpar dword, kbytes
	%xdefine kbytes bp + ?kbytes
	lenter

%if METHOD EQ 1
                assume ds:nothing
                test word [kbytes+2],-1       ; Over 0FFFFh K is impossible
                jnz @@fail
                mov bx,word ptr [kbytes]          ; New disk size
                mov dx,[cs:XMS_alloc + XMS_handle]     ; Handle to the memory
                mov ah,0Fh                      ; Reallocate
                or dx,dx
                jnz @@alloc                       ; If no handle, then
                  mov dx,bx                     ; allocate
                  mov ah,9
@@alloc:
                call far [cs:XMS_alloc + XMS_entry]
                or ax,ax
                jnz @@ok

@@fail:           mov ax,word ptr [cs:conf + c_size]     ; Fail, return current
                xor dx,dx
	lleave code
                ret

@@ok:             mov ax,word ptr [kbytes]          ; Ok, return requested
                xor dx,dx
	lleave code
                ret
                assume ds:d_seg
%endif

%if METHOD EQ 2
                assume ds:nothing

                mov ax,word ptr [kbytes]          ; New disk size
                mov dx,word ptr [kbytes+2]
                cmp dx,15*1024
                jae @@fail

                mov cx,16*1024
                div cx
                or dx,dx
                jz @@no_roundup
                  inc ax
@@no_roundup:     mov bx,ax
                mov dx,[cs:EMS_alloc + EMS_handle]     ; Handle to the memory
                or ax,ax
                jz free
                cmp dx,-1                       ; Is the handle valid?
                jne @@realloc
                  mov ah,EMS_ALLOCATE
                  int 67h
                  or ah,ah
                  jnz @@fail
                  mov [cs:EMS_alloc + EMS_handle],dx
                  jmp @@ok
free:
                mov ah,EMS_DEALLOCATE
                int 67h
                or ah,ah
                jnz @@fail
                mov [cs:EMS_alloc + EMS_handle],dx
                jmp @@ok
@@realloc:
                mov ah,EMS_REALLOCATE_PAGES     ; Reallocate
                int 67h
                or ah,ah
                jnz @@fail

@@ok:             mov ax,word ptr [kbytes]          ; Ok, return requested
                xor dx,dx
	lleave code
                ret

@@fail:           mov ax,word ptr [cs:conf + c_size]     ; Fail, return current
                xor dx,dx
	lleave code
                ret

                assume ds:d_seg
%endif

%if METHOD EQ 3
                assume ds:nothing

                mov ax,word ptr [kbytes]          ; New disk size
                mov dx,word ptr [kbytes+2]
                cmp dx,15*1024
                jae @@fail

                mov cx,16*1024
                div cx
                or dx,dx
                jz @@no_roundup
                  inc ax
@@no_roundup:     mov bx,ax
                mov dx,[cs:EMS_alloc + EMS_handle]     ; Handle to the memory
                or ax,ax
                jz @@free
                cmp dx,-1                       ; Is the handle valid?
                jne @@realloc
                  mov ah,EMS_ALLOCATE
                  int 67h
                  or ah,ah
                  jnz @@fail
                  mov [cs:EMS_alloc + EMS_handle],dx
                  jmp @@ok
@@free:
                mov ah,EMS_DEALLOCATE
                int 67h
                or ah,ah
                jnz @@fail
                mov [cs:EMS_alloc + EMS_handle],dx
                jmp @@ok
@@realloc:                                        ; !!!! Not a 3.2 call!
                mov ah,EMS_REALLOCATE_PAGES     ; Reallocate
                int 67h
                or ah,ah
                jnz @@fail

@@ok:             mov ax,word ptr [kbytes]          ; Ok, return requested
                xor dx,dx
	lleave code
                ret

@@fail:           mov ax,word ptr [cs:conf + c_size]     ; Fail, return current
                xor dx,dx
	lleave code
                ret

                assume ds:d_seg
%endif
	%undef kbytes
	lleave ctx

malloc          endp

;**************************************************************************
;
;               QUERY FREE MEMORY
;
; This routine is only used if CAPABLE has C_NOALLOC bit clear.
;
; On entry
;
; Preserve
;   ds, si, di, bp and stack contents
;
; Return
;   - dx:ax set to maximum safe size to reallocate to
;   - cx:bx set to maximum size to reallocate to (may not be valid)
;
;**************************************************************************

freemem         proc far

%if METHOD EQ 1
                assume ds:nothing
                mov ah,8
                call far [cs:XMS_alloc + XMS_entry]
                xor dx,dx
                mov bx,ax
                mov cx,dx
                add bx,word ptr [conf.size]
                adc cx,word ptr [conf.size+2]
                ret
                assume ds:d_seg
%endif

%if METHOD EQ 2
                assume ds:nothing
                mov ah,EMS_FREE_PAGE_COUNT
                int 67h                 ; bx = unallocated pages
                mov ax,16               ; of 16K each
                mul bx                  ; dx:ax = total free K
                add ax,word ptr [conf.size]
                adc dx,word ptr [conf.size+2]
                mov bx,ax
                mov cx,dx
                ret
                assume ds:d_seg
%endif

%if METHOD EQ 3
                assume ds:nothing
                mov ah,EMS_FREE_PAGE_COUNT
                int 67h                 ; bx = unallocated pages
                mov ax,16               ; of 16K each
                mul bx                  ; dx:ax = total free K
                add ax,word ptr [conf.size]
                adc dx,word ptr [conf.size+2]
                mov bx,ax
                mov cx,dx
                ret
                assume ds:d_seg
%endif

freemem         endp

%else    ; ife CAPABLE and C_NOALLOC

freemem equ 0
%endif   ; ife CAPABLE and C_NOALLOC

ret_far proc far                        ; Null function to replace
        ret                             ;  strategy and command when appended
ret_far endp                            ;  to another SRDISK drive

%if HOOKINT19

;**************************************************************************
;
;                       Warm Boot of Machine
;
; Release used XMS memory on warm boot.
;
; I guess this may be important if some virtual machine (VM) in some
; multitasking system has installed this driver and the VM is ended.
; Without this the other VMs could lose the space reserved for RAM disk
; in this VM.
;**************************************************************************

iispentry int_19_entry		; INT 19 boot function
old_int19: equ .next

%if METHOD EQ 1
                assume ds:nothing
                push ax
                push bx
                push dx
                push ds
                pushf
                mov     dx,[cs:XMS_alloc + XMS_handle]
                test    dx,dx
                jz      @@int19_1                 ; Jump if no XMS handle
		mov	ah,0Ah
                call    far [cs:XMS_alloc + XMS_entry]     ; Free XMS memory
@@int19_1:
                xor     ax,ax
                mov     word [cs:XMS_alloc + XMS_handle], ax
		mov	ds,ax
                mov     ax,[cs:old_int19 + 0]
		cli				; Disable interrupts
                mov     [19h*4],ax           ; for the time to write
                mov     ax,[cs:old_int19 + 2]        ; old interrupt vector back
                mov     [19h*4+2],ax
                popf
                pop ds
                pop dx
                pop bx
                pop ax
                jmp     far [cs:old_int19]
                assume ds:d_seg
%endif

%if METHOD EQ 2
                assume ds:nothing
                push ax
                push dx
                push ds
                pushf
                mov dx,[cs:EMS_alloc + XMS_handle]
                cmp dx,-1
                jne @@int19_1                     ; Jump if no EMS handle
                mov ah,EMS_DEALLOCATE
                int 67h                         ; Free EMS memory
                or word [cs:EMS_alloc + EMS_handle],-1
@@int19_1:
                xor ax,ax
                mov ds,ax
                mov ax,[cs:old_int19 + 0]
		cli				; Disable interrupts
                mov [19h*4],ax               ; for the time to write
                mov ax,[cs:old_int19 + 2]            ; old interrupt vector back
                mov [19h*4+2],ax
                popf
                pop ds
                pop dx
                pop ax
                jmp far [cs:old_int19]
                assume ds:d_seg
%endif

%if METHOD EQ 3
                assume ds:nothing
                push ax
                push dx
                push ds
                pushf
                mov dx,[cs:EMS_alloc + XMS_handle]
                cmp dx,-1
                jne @@int19_1                     ; Jump if no EMS handle
                mov ah,EMS_DEALLOCATE
                int 67h                         ; Free EMS memory
                or word [cs:EMS_alloc + EMS_handle],-1
@@int19_1:
                xor ax,ax
                mov ds,ax
                mov ax,[cs:old_int19 + 0]
		cli				; Disable interrupts
                mov [19h*4],ax               ; for the time to write
                mov ax,[cs:old_int19 + 2]            ; old interrupt vector back
                mov [19h*4+2],ax
                popf
                pop ds
                pop dx
                pop ax
                jmp far [cs:old_int19]
                assume ds:d_seg
%endif

int_19_entry    endp
%endif   ; HOOKINT19

                      ; Memory specific resident data
;**************************************************************************
;
;                       Local data
;
; This data is used by the two above routines that are needed
; resident in any case.
;
;**************************************************************************

%if METHOD EQ 1
XMS_alloc: ; NASM structure instance
XMS_alloc_s_size equ XMS_alloc_s_struc_size	; NASM port equate
istruc XMS_alloc_s
iend

%if CAPABLE & C_NOALLOC
malloc EQU offset XMS_alloc
%endif
%endif

%if METHOD EQ 2
EMS_alloc: ; NASM structure instance
EMS_alloc_s_size equ EMS_alloc_s_struc_size	; NASM port equate
istruc EMS_alloc_s
at EMS_handle,	dw -1
iend

%if CAPABLE & C_NOALLOC
malloc EQU offset EMS_alloc
%endif
%endif

%if METHOD EQ 3
EMS_alloc: ; NASM structure instance
EMS_alloc_s_size equ EMS_alloc_s_struc_size	; NASM port equate
istruc EMS_alloc_s
at EMS_handle,	dw -1
iend

%if CAPABLE & C_NOALLOC
malloc EQU offset EMS_alloc
%endif
%endif


;**************************************************************************
;
;                       Debugging code
;
; This code prints out a character and a word in hex. This code can be
; used using "debug char,word" macro in the code to give some output of
; the actions device driver is doing.
;
; A color display is assumed with 80 characters on a row and 25 rows.
;
;**************************************************************************

%if DEBUGGING
                assume ds:nothing

debug_c         proc near
                push es
                push di

                mov ah,[cs:d_attr]           ; Load color attribute
                mov di,0B800h           ; Load screen segment (assumes color)
                mov es,di
                mov di,[cs:d_loc]            ; Load line
                cmp di,26*160           ; Below screen?
                jb debug_c1
                  mov di,2*160          ; Yes, move to third line (for scroll off)
                  mov [cs:d_loc],di
                  add ah,10h            ; Change color
                  cmp ah,70h
                  jb debug_c2
                    sub ah,60h
debug_c2:         mov [cs:d_attr],ah

                  push es               ; Wait if shift down
                  push ax
                  mov ax,40h
                  mov es,ax
debug_c3:         test byte ptr [es:17h],3
                  jnz debug_c3
                  pop ax
                  pop es
debug_c1:
                add di,[cs:d_col]            ; Advance to right column
                mov [es:di],ax          ; Print error letter

                call debug_x            ; Print high byte
                mov dh,dl
                call debug_x            ; Print low byte

                add word [cs:d_loc],160           ; Next line

                pop di
                pop es
                ret
debug_c         endp

debug_x         proc near               ; Print a byte in hex
                mov al,dh
                shr al,1
                shr al,1
                shr al,1
                shr al,1
                call debug_x1
                mov al,dh
debug_x1:       and al,0Fh              ; Print a hex digit
                cmp al,10
                jae debug_x2
                  add al,'0'
                  jmp debug_x3
debug_x2:       add al,'A'-10
debug_x3:
                inc di
                inc di
                mov [es:di],ax
                ret
debug_x         endp

d_loc   dw 2*160
d_col   dw 150
d_attr  db 40h

                assume ds:d_seg

%endif ; DEBUGGING


	align 2, db 0
ctrl0:		dd 0			; far pointer to first driver
	; This is initialised from int 2Dh, except if no prior
	;  driver is resident. In the latter case, it is made to
	;  point to this driver instance itself.
	; We don't use a reference to an AMIS multiplex number
	;  because those can change from the renumber utility.
	; If we add uninstall support, then the ctrl0 of all
	;  remaining resident instances must be updated so they
	;  no longer point to an about to be uninstalled instance.

amis_sign:
	fill 8, 32, db "ecm"
	fill 8, 32, db "SRDISK"
	asciz "reSizeable RAM Disk"

intlist:
%if HOOKINT19
	db 19h
	dw int_19_entry
%endif
	db 2Dh
	dw i2D

iispentry i2D
	cmp ah, 0
.multiplexnumber: equ $ - 1
	je .ours
	jmp far [cs:.next]

.ours:
	cmp al, 0
	je .installed
	cmp al, 4
	je .intlist
	cmp al, 6
	je .device
	cmp al, 10h
	je .getcompatible
	cmp al, 20h
	je .ctrl0
	mov al, 0
	iret

.installed:
	mov cx, 200h + 10		; v2.10
	mov di, amis_sign
.iret_dx_al:
	mov al, -1
.iret_dx:
	mov dx, cs
	iret

.intlist:				; al == 04h already
	mov bx, intlist
	jmp .iret_dx

.device:
	mov ax, 0101h			; ah = flags
		; (loaded from config, linked into chain, inreentrant)
					; al = number of headers
	xor bx, bx
	jmp .iret_dx			; dx:bx -> header

.getcompatible:
	mov al, -1
	mov cx, 200h + 10		; v2.10
	iret

.ctrl0:
	mov al, 4
	mov bx, ctrl0
	jmp .iret_dx


;**************************************************************************
;
;                       Configuration tables
;
; This structure holds all the formatting data used by the formatter.
; The formatter is passed a pointer to this data and it modifies it
; directly. For this arrangement to work THE BELOW TABLE MAY NOT BE
; MODIFIED WITHOUT PROPER CHANGES IN SRDISK.EXE. dr_v_format contains
; version number which is to be changed when a change is made to this
; structure.
;
; Only the first fields up to label appended_eor is resident and used in
; in every case. The rest is used only in the main driver in a chain of
; RAM disk drivers.
;
; !!! The formatter may use any initial values in this structure as
;     default values i.e. set all needed values here !!!
; !!! A DR-DOS bug must be avoided by defining c_BPB_sectors and
;     c_BPB_FATsectors so that they could be real !!!
;**************************************************************************

READ_ACCESS     equ     1       ; Bit masks for the RW_access
WRITE_ACCESS    equ     2

config_s struc
c_drive         db      ?               ; Drive letter
c_flags         db      ? ; CAPABLE         ; Misc capability flags
c_disk_IO       dw      ? ; offset disk_IO  ; disk_IO entry
c_disk_IO_seg   dw      ? ; 0               ; disk_IO entry segment
c_malloc        dw      ? ; malloc          ; malloc entry offset (in DS/CS)
c_freemem       dw      ? ; freemem         ; free mem query entry offset (in DS/CS)
c_next          dw      ? ; 0               ; Next driver in chain (segment)
c_maxK          dd      ? ; MAX_SIZE        ; Maximum allowed size
c_size          dd      ? ; 0               ; Current allocated size in Kbytes
c_allocblock    dw      ? ; ALLOCBLOCK      ; Memory allocation block size
c_sectorsl      dw      ? ; 0               ; Sectors in this driver (low word)
c_sectorsh      dw      ? ; 0               ; Sectors in this driver (high word)
c_bps_shift     db      ? ; 7               ; 2^c_bps_shift == c_BPB_bps

c_BPB_bps       dw      ? ; 512             ; Sector size
c_BPB_spc       db      ? ; 1               ; Cluster size in sectors
c_BPB_reserved  dw      ? ; 1               ; The boot sector is reserved
c_BPB_FATs      db      ? ; 1               ; One FAT copy
c_BPB_dir       dw      ? ; 64              ; 64 entries in root directory
c_BPB_sectors   dw      ? ; 100             ; number of sectors on 16-bit disk
c_BPB_media     db      ? ; 0FAh            ; Media is RAM DISK
c_BPB_FATsectors dw     ? ; 1               ; Sectors per one FAT
c_BPB_spt       dw      ? ; 8               ; Sectors per track
c_BPB_heads     dw      ? ; 1               ; Number of heads
c_BPB_hiddenl   dw      ? ; 0               ; # of hidden sectors (low word)
c_BPB_hiddenh   dw      ? ; 0               ; # of hidden sectors (high word)
c_BPB_tsectorsl dw      ? ; 0               ; # of sectors on 32-bit disk (low word)
c_BPB_tsectorsh dw      ? ; 0               ; # of sectors on 32-bit disk (high)

c_tsize         dd      ? ; 0               ; Total size in Kbytes (all drivers)

c_RW_access     db      ? ; 000b            ; B0 = read, B1 = write, B2 = fixed
c_media_change  db      ? ; 1               ; -1 if media changed, 1 if not
c_device_type   db      ? ; 0               ; Device type for Generic IOCTL
c_open_files    dw      ? ; 0               ; Files currently open on drive
c_next_drive    dw      ? ; 0               ; Segment of next SRDISK drive
c_transfer      dw      ? ; batch_xfer      ; Batch xfer function address
config_s ends

	align 2, db 0
;conf            config_s <>
; TASM 3.0 bugs force this approach:
conf: ; NASM structure instance
config_s_size equ config_s_struc_size	; NASM port equate
istruc config_s
at c_drive
	db 0
at c_flags
	db CAPABLE
at c_disk_IO
	dw offset disk_IO
at c_disk_IO_seg
	dw 0
at c_malloc
	dw malloc
at c_freemem
	dw freemem
at c_next
	dw 0
at c_maxK
	dd MAX_SIZE
at c_size
	dd 0
at c_allocblock
	dw ALLOCBLOCK
at c_sectorsl
	dw 0
at c_sectorsh
	dw 0
at c_bps_shift
	db 9
at c_BPB_bps
	dw 512
at c_BPB_spc
	db 1
at c_BPB_reserved
	dw 1
at c_BPB_FATs
	db 1
at c_BPB_dir
	dw 64
at c_BPB_sectors
	dw 100
at c_BPB_media
	db 0FAh
at c_BPB_FATsectors
	dw 1
at c_BPB_spt
	dw 8
at c_BPB_heads
	dw 1
at c_BPB_hiddenl
	dw 0
at c_BPB_hiddenh
	dw 0
at c_BPB_tsectorsl
	dw 0
at c_BPB_tsectorsh
	dw 0
at c_tsize
	dd 0
at c_RW_access
	db 0
at c_media_change
	db 1
at c_device_type
	db 0
at c_open_files
	dw 0
at c_next_drive
	dw 0
at c_transfer
	dw batch_xfer
iend

appended_eor    equ offset conf + c_BPB_spc ; End of resident for appended driver


;**************************************************************************
;
;               Other internal and resident data
;
; The order of this data is not significant as it will not be used outside
;
;**************************************************************************

	align 2, db 0
labelsize BPB, byte, conf + c_BPB_bps
pBPB            dw offset BPB           ; Pointer to BPB (for cmd_init)

req_ptr         dd 0                    ; Request structure pointer

%if STACKSIZE
caller_sp       dw 0                    ; To store pointer to callers stack
caller_ss       dw 0                    ;   if we use our own stack
%endif

                ; Pointers to commands
command_tbl     dw cmd_init             ;  0 Init
                dw cmd_media            ;  1 Media
                dw cmd_BPB              ;  2 Build BPB
                dw cmd_unknown          ;  3 IOCTL input
                dw cmd_input            ;  4 Input
                dw cmd_unknown          ;  5 nondest input (char)
                dw cmd_unknown          ;  6 input status (char)
                dw cmd_unknown          ;  7 input flush (char)
                dw cmd_output           ;  8 Output
                dw cmd_output           ;  9 Output with verify
                dw cmd_unknown          ; 10 output status (char)
                dw cmd_unknown          ; 11 output flush (char)
                dw cmd_unknown          ; 12 IOCTL output
                dw cmd_open             ; 13 Open device
                dw cmd_close            ; 14 Close device
                dw cmd_removable        ; 15 Removable media check
%if CAPABLE & C_GIOCTL
                dw cmd_unknown          ; 16 Output untill busy (char)
                dw cmd_unknown          ; 17
                dw cmd_unknown          ; 18
                dw cmd_GIOCTL           ; 19 Generic IOCTL
                dw cmd_unknown          ; 20
                dw cmd_unknown          ; 21
                dw cmd_unknown          ; 22
                dw cmd_getLogical       ; 23
                dw cmd_setLogical       ; 24
%endif

high_cmd        db ($-offset command_tbl)/2  ; Number of supported commands

;**************************************************************************
;
;                       Batch transfer of sectors
;
; This transfers sectors according to the parameters passed on stack
; even if the sectors are on different drivers chained to this one.
;
; Returns how many sectors were NOT moved; ax = 0 means all OK.
;
; !!! This function destroys the contents of DS !!!
;
;**************************************************************************

batch_xfer      proc far
 %if 0
                arg buffer:far ptr, sector:dword, count:word, RW:word
                local byteoffset:dword, bytes:word
 %endif
	lframe far
	lpar word, ?RW
	lpar word, ?count
	lpar dword, ?sector
	lpar dword, ?buffer
	 %xdefine RW bp + ??RW
	 %undef count
	 %xdefine count bp + ??count
	 %xdefine sector bp + ??sector
	 %xdefine buffer bp + ??buffer
	lvar dword, ?byteoffset
	lvar word, ?bytes
	 %xdefine byteoffset bp + ??byteoffset
	 %undef bytes
	 %xdefine bytes bp + ??bytes
	lenter , sub

                debug 'o',<word ptr buffer>
                debug 's',<word ptr buffer+2>

%if CAPABLE & C_GIOCTL
                test word [RW],-1                      ; Writing boot sector?
                jz batch_notboot
                mov ax,word ptr [sector]
                or ax,word ptr [sector+2]
                jnz batch_notboot

                mov ds,word ptr [buffer+2]
                assume ds:nothing
                mov si,word ptr [buffer]
                add si,bsBytesPerSec
                mov ax,[si]
                cmp ax,[cs:conf + c_BPB_bps]
                jnz batch_notboot

                push cs                         ; Copy new BPS to conf
                pop es
                mov di,offset conf + c_BPB_bps

                mov cx,25
                rep movsb

                mov byte [cs:conf + c_media_change],0       ; Tell do not know if changed
batch_notboot:
%endif
                push cs
                pop ds

                mov si,offset conf
                ; DS:SI - conf of current driver (main here)
                assume ds:nothing

batch_next_driver:
                ; Transfer sector to byte offset to disk and count the
                ; number of bytes to xfer.
                mov ax,word ptr [sector]
                mov dx,word ptr [sector+2]
                mov cx,[count]
                debug 'a',ax
                debug 'd',dx
                debug 'c',cx

                cmp ax,[si + c_sectorsl]          ; Starting sector on disk?
                sbb dx,[si + c_sectorsh]
                mov dx,word ptr [sector+2]
                jae batch_to_next               ; No

                add ax,cx                       ; Count ending sector
                adc dx,0

                cmp dx,[si + c_sectorsh]          ; Ending sector on disk?
                jb batch_IO1                    ; Jump if is
                sub ax,[si + c_sectorsl]
                jbe batch_IO1                   ; Jump if is

                sub cx,ax                       ; Count how many we CAN transfer
                debug 'T',cx
batch_IO1:
                push cx
                mov ax,cx                       ; Count number of bytes to move
                ;mul [si].c_BPB_bps
                mov cl,[si + c_bps_shift]
                sal ax,cl
                mov word ptr [bytes],ax           ; Number of bytes to move

                mov ax,word ptr [sector+2]        ; Count starting byte
                ;mul [si].c_BPB_bps
                sal ax,cl
                mov cx,ax
                mov ax,word ptr [sector]
                mul word [si + c_BPB_bps]
                add dx,cx                       ; dx:ax is starting byte
                mov word ptr [byteoffset],ax
                mov word ptr [byteoffset+2],dx

                call far [si + c_disk_IO]
                pop ax                          ; Pop number of sectors
                jc batch_IOx                    ; I/O error

                sub [count],ax                    ; All transferred?
                jbe batch_done                  ;  yes, so exit

%if CAPABLE & C_APPENDED
                debug 'N',ax
                push ax
                ;mul [si].c_BPB_bps              ; Increment transfer offset
                mov cl,[si + c_bps_shift]
                sal ax,cl
                add word ptr [buffer],ax          ;  which can not exceed 0FFFFh
                pop ax

                xor dx,dx
                add ax,word ptr [sector]          ; Count new starting sector
                adc dx,word ptr [sector+2]
batch_to_next:
                sub ax,[si + c_sectorsl]          ; Subtract passed sectors
                sbb dx,[si + c_sectorsh]
                mov word ptr [sector],ax          ; And store the result
                mov word ptr [sector+2],dx

                mov si,[si + c_next]              ; Find next driver
                or si,si
                mov ds,si
                mov si,[dr_conf]
                jz batch_err
                jmp batch_next_driver

%else    ;if CAPABLE and C_APPENDED

batch_to_next:

%endif   ;if CAPABLE and C_APPENDED

batch_err:
                ; there is no next driver - sectors not found!
                debug 'E',count
                stc
                jmp batch_IOx
batch_done:
                clc
batch_IOx:
                mov ax,[count]
	lleave
                ret

	 %undef RW
	 %undef count
	 %undef sector
	 %undef buffer
	 %undef bytesoffset
	 %undef bytes

                assume ds:d_seg
batch_xfer      endp

  
;**************************************************************************
;
;                       Set request header address
;
; Called by DOS to set the request structure pointer
;
;**************************************************************************
  
strategy        proc far
                mov word ptr [cs:req_ptr],bx
                mov word ptr [cs:req_ptr+2],es
                ret
strategy	endp
  
  
;**************************************************************************
;
;                       Commands
;
; Called by DOS. Requested action defined in structure pointed by req_ptr.
;
;**************************************************************************
  
commands        proc far
                assume ds:nothing
                pushf
                push ax
                push bx
                push cx
                push dx
                push si
                push di
                push ds
                push es

                %if STACKSIZE
                ; Switch into our own stack
                mov [cs:caller_ss],ss
                mov [cs:caller_sp],sp
                push cs
                pop ss
                mov sp, offset end_of_resident
.patchstack: equ $ - 2
                %endif

		%if DEBUGGING
                push bp
                mov bp,sp
                %endif

                cld
		lds si,[cs:req_ptr]
                assume ds:nothing
		; We trust Microsoft that the unit is right at [si].rhUnit
		mov cx,[si + rwrBytesSec] 	; Sectors/Cmd line/BPB pointer
		mov dx,[si + rwrStartSec] 	; Start sector/Device number
		mov bl,[si + rhFunction]
                xor bh,bh                       ; Count index to command_tbl
                debug 'F',bx
                cmp bl,[cs:high_cmd]                 ; Is command supported?
                jae cmd_unknown                 ; Jump if not
		shl bx,1
		les di,[si + rwrBuffer]		; ES:DI = transfer address
		push cs
		pop ds				; DS to local data segment
                assume ds:d_seg
		jmp [command_tbl + bx]		; Do command
cmd_unknown:
                assume ds:nothing
		mov ax,ERROR | UNKNOWN_COMMAND
		jmp cmd_ret
cmd_IOerr:
		lds bx,[cs:req_ptr]
		mov word [bx + rwrBytesSec],0		; Sector count zero
cmd_error:
		mov ah, (ERROR | DONE) >> 8	; ERROR and DONE (err #, in al)
		jmp cmd_ret

cmd_ok:
		mov ax,DONE
cmd_ret:
                debug 'Q',ax
		lds bx,[cs:req_ptr]
		mov [bx + rhStatus],ax

                %if DEBUGGING
                cmp bp,sp
                je dbstack1
                debug '/',sp
                debug '/',bp
dbstack1:
                mov sp,bp
                pop bp
                %endif

                %if STACKSIZE
                ; Switch back into callers stack
                mov ss,[cs:caller_ss]
                mov sp,[cs:caller_sp]
;                cmp stack_check,STACK_CHECK_ID
;                je cmd_stack_ok
                   ;report error
;cmd_stack_ok:
                %endif

		pop es
		pop ds
		pop di
		pop si
		pop dx
		pop cx
		pop bx
		pop ax
                popf
		retf				; Return far
                assume ds:d_seg
commands	endp

;**************************************************************************
;
;               Media Check command
;
;**************************************************************************

cmd_media       proc near
                les bx,[req_ptr]
                mov al,[conf + c_media_change]      ; Read the change return
                debug 'C',ax

                test byte [conf + c_RW_access],READ_ACCESS
                jz dev_not_ready

		mov [es:bx + mrReturn],al
		mov word ptr [es:bx + mrVolumeID],offset dr_volume
		mov word ptr [es:bx + mrVolumeID + 2],cs
                jmp cmd_ok

dev_not_ready:
                mov al,DRIVE_NOT_READY
                jmp cmd_error

cmd_media       endp


;**************************************************************************
;
;               Build BPB command
;
;**************************************************************************

cmd_BPB         proc near
                debug 'B',<conf.c_RW_access>
                test byte [conf + c_RW_access],READ_ACCESS
                jz dev_not_ready

                les bx,[req_ptr]
                mov word ptr [es:bx + bbrBPBAddress],offset BPB
                mov word ptr [es:bx + bbrBPBAddress + 2],cs
                mov word [conf + c_open_files],0         ; Reset open files to 0
                mov byte [conf + c_media_change],1       ; Media no longer changed
                jmp cmd_ok
cmd_BPB         endp


;**************************************************************************
;
;               Device Open command
;
;**************************************************************************

cmd_open        proc near
                debug 'O',-1
                inc word [conf + c_open_files]
                jmp cmd_ok
cmd_open        endp


;**************************************************************************
;
;               Device Close command
;
;**************************************************************************

cmd_close       proc near
                cmp word [conf + c_open_files],0
                jz cmd_close1
                dec word [conf + c_open_files]
cmd_close1:
                jmp cmd_ok
cmd_close       endp


;**************************************************************************
;
;               Removable media query
;
;**************************************************************************

cmd_removable   proc near
                mov ax,DONE
                test byte [conf + c_RW_access],4
                jz cmd_removable_x
                or ax,BUSY
cmd_removable_x:
                jmp cmd_ret
cmd_removable   endp

;**************************************************************************
;
;               INPUT command
;
;**************************************************************************

cmd_input       proc near
                debug 'R',0
                test byte [conf + c_RW_access],READ_ACCESS
                jz cmd_input1
                xor bl,bl
                push bx                         ; Last parameter to batch_xfer
                jmp cmd_io
cmd_input1:
                mov al,DRIVE_NOT_READY
                jmp cmd_IOerr
cmd_input       endp


;**************************************************************************
;
;               OUTPUT command
;
;**************************************************************************

cmd_output      proc near
                debug 'W',0
                mov al,WRITE_PROTECT
                mov bl,1
                test byte [conf + c_RW_access],WRITE_ACCESS
                jnz to_cmd_io
                  jmp cmd_outerr
to_cmd_io:      push bx                         ; Last parameter to batch_xfer

cmd_io:
                ; CX    - sectors
                ; ES:DI - transfer address
                ; DS    = CS

                mov ax,cx                       ; Count number of bytes to move
                mul word [conf + c_BPB_bps]
                jc cmd_output4                  ; Is it too much? (dx != 0)

                ; check transfer address and count that they do not
                ; exceed segment limit
                add ax,di                       ; (dx = 0 after the mul)
                jnc cmd_output5
cmd_output4:
                mov ax,di                       ; How many bytes CAN we move?
                xor ax,-1
                ;xor dx,dx                       ; How many sectors?
                ;div conf.c_BPB_bps
                mov cl,[conf + c_bps_shift]
                shr ax,cl
                mov cx,ax
cmd_output5:
                push cx                         ; count parameter to batch_xfer
                                
;                mov cx,es
;                les si,req_ptr
;                mov [es:si].rwrBytesSec,dx     ; Xferred 0 sectors so far
;                cmp [es:si].rwrLength,1Ah        ; Do we use 32-bit sec. address?
;                jbe cmd_output3
;                  debug '3',0
;                  push word ptr [es:si].rwrHugeStartSec+2
;                  push word ptr [es:si].rwrHugeStartSec
;                  jmp cmd_output8
;cmd_output3:
;                xor dx,dx                       ; 16 bit sector parameter
;                push dx                         ;  to batch_xfer
;                push [es:si].rwrStartSec

                mov cx,es
                les si,[req_ptr]
                mov ax,[es:si + rwrStartSec]
                cmp ax,-1
                jne cmd_output3
                  debug '3',0
		  push word ptr [es:si + rwrHugeStartSec + 2]
		  push word ptr [es:si + rwrHugeStartSec]
                  jmp cmd_output8
cmd_output3:
                xor dx,dx                       ; 16 bit sector parameter
                push dx                         ;  to batch_xfer
                push ax

cmd_output8:
                push cx                         ; buffer parameter to batch_xfer
                push di

		push cs
                call batch_xfer
                assume ds:nothing
                jc cmd_output1
                add sp,12

                les si,[cs:req_ptr]
		sub [es:si + rwrBytesSec],ax     ;  actually transferred
cmd_output6:
                jmp cmd_ok

cmd_output1:
                add sp,12
                debug 'S',ax
                mov al,SECTOR_NOT_FOUND
cmd_outerr:
                jmp cmd_IOerr
                assume ds:d_seg
cmd_output      endp
  

%if 0
;**************************************************************************
;
;               IOCTL write
;
; This function is used to switch to "inside DOS" and avoid some
; multitaskin memory manager problems. We hope that the multitasker does
; not free the memory if it is allocated here.
;
; The actual service routine is located in the caller at the address
; where the data location is. Data size must be IOCTL_ID.
;
;**************************************************************************

cmd_IOCTL_write proc near
                debug 'i',cx
                cmp cx,IOCTL_ID         ; cx is the count
                je cmd_iow1
                  jmp cmd_unknown
cmd_iow1:
                lds si,[req_ptr]
                assume ds:nothing
                call [si + irwrBuffer]
                jmp cmd_ok
                assume ds:d_seg
cmd_IOCTL_write endp
%endif


;**************************************************************************
;
;               GENERIC IOCTL
;
;**************************************************************************

	align 16, nop
%if CAPABLE & C_GIOCTL
%if STACKSIZE
noGIOCTL_eor equ offset $ + STACKSIZE   ; End of resident when no GIOCTL
                                        ; but when we have stack!
%else
noGIOCTL_eor equ offset $       ; End of resident when GIOCTL not supported
%endif

GIOCTL_MEDIA equ FALSE          ; If true, MID info is handled

%if GIOCTL_MEDIA
media_ID: ; NASM structure instance
MID_size equ MID_struc_size	; NASM port equate
istruc MID
at midInfoLevel
	dw 0
at midSerialNum
	dd 12345678h
at midVolLabel
	times 11 db "SRDISK     "
at midFileSysType
	times 8 db "FAT12   "
iend
%endif

gioctl_func_s struc
gioctl_fcode    db ?
gioctl_faddr    dw ?
gioctl_func_s ends

gioctl_func: ; NASM structure instance
gioctl_func_s_size equ gioctl_func_s_struc_size	; NASM port equate
istruc gioctl_func_s
at gioctl_fcode
	db 41h
at gioctl_faddr
	dw offset gioctl_write
iend
istruc gioctl_func_s
at gioctl_fcode
	db 60h
at gioctl_faddr
	dw offset gioctl_getparam
iend
istruc gioctl_func_s
at gioctl_fcode
	db 61h
at gioctl_faddr
	dw offset gioctl_read
iend
istruc gioctl_func_s
at gioctl_fcode
	db 42h
at gioctl_faddr
	dw offset cmd_ok
iend
istruc gioctl_func_s
at gioctl_fcode
	db 62h
at gioctl_faddr
	dw offset cmd_ok
iend
;               gioctl_func_s <40h, offset gioctl_setparam>
%if GIOCTL_MEDIA
istruc gioctl_func_s
at gioctl_fcode
	db 46h
at gioctl_faddr
	dw offset gioctl_setmedia
iend
istruc gioctl_func_s
at gioctl_fcode
	db 66h
at gioctl_faddr
	dw offset gioctl_getmedia
iend
%endif
gioctl_func_end label byte


cmd_GIOCTL      proc near
                les si,[req_ptr]
		cmp byte [es:si + giCategory],8
                jnz cmd_gioerr1

		mov al,[es:si + giMinorCode]
                debug 'G',ax

		les si,[es:si + giIOCTLData]	; Parameter block
                mov bx,offset gioctl_func
cmd_gionfunc:
                cmp [bx + gioctl_fcode],al
                je cmd_giodofunc
                add bx,gioctl_func_s_struc_size
                cmp bx,offset gioctl_func_end
                jb cmd_gionfunc
cmd_gioerr1:
                jmp cmd_unknown
cmd_giodofunc:
                jmp [bx + gioctl_faddr]           ; Call with ES:SI -> param block

gioctl_dev_not_ready:
                mov al,DRIVE_NOT_READY
                jmp cmd_error

cmd_GIOCTL      endp

;******** GIOCTL write (41h)

gioctl_write    proc near
                test byte [conf + c_RW_access],WRITE_ACCESS
                jz gioctl_write_prot

                mov al,1
                push ax                         ; RW parameter to batch_xfer
                jmp track_xfer

gioctl_write_prot:
                mov al,WRITE_PROTECT
                jmp cmd_error
gioctl_write    endp

;******** GIOCTL read (61h)

gioctl_read     proc near
                test byte [conf + c_RW_access],READ_ACCESS
                jz gioctl_dev_not_ready

                mov al,0
                push ax                         ; RW parameter to batch_xfer
track_xfer:
                ; !!!! Expects c_BPB_heads * cylinders
                ; and c_BPB_heads * c_BPB_spt less than 65536
		push word [es:si + rwSectors]
		mov ax,[es:si + rwCylinder]
                mul word [conf + c_BPB_heads]
                mul word [conf + c_BPB_spt]
                mov bx,ax
                mov cx,dx
		mov ax,[es:si + rwHead]
                mul word [conf + c_BPB_spt]
		add ax,[es:si + rwFirstSector]
                add ax,bx
                adc dx,cx
                push dx                         ; absolute sector
                push ax

		push word ptr [es:si + rwBuffer + 2] ; transfer address
		push word ptr [es:si + rwBuffer]

		push cs
                call batch_xfer                 ; Destroys DS
                add sp,12
                jc track_xfer_IOerr

                jmp cmd_ok

track_xfer_IOerr:
                mov al,SECTOR_NOT_FOUND
                jmp cmd_error
gioctl_read     endp

;******** GIOCTL set parameters (40h)


%if 0
gioctl_setparam proc near
;               test conf.c_RW_access,READ_ACCESS       ; !!!! Is this needed?
;               jz gioctl_dev_not_ready

                debug '0',<word ptr [es:si]>
                debug '2',<word ptr [es:si+2]>
                debug '4',<word ptr [es:si+4]>
                debug '6',<word ptr [es:si+6]>
                debug 'R',<word ptr [es:si].dpRootDirEnts>
                debug 'S',<word ptr [es:si].dpSectors>  ; Sectors
		debug 'M',<word ptr [es:si].dpMedia>    ; Media
;               test byte ptr [es:si].dpSpecFunc,3 ; Change format?
;               jnz gioctl_bad_media
;               jmp cmd_ok
gioctl_bad_media:
                mov al,UNKNOWN_MEDIA
                jmp cmd_error
gioctl_setparam endp
%endif

;******** GIOCTL get parameters (60h)

gioctl_getparam proc near
;               test conf.c_RW_access,READ_ACCESS       ; !!!! Is this needed?
;               jz gioctl_dev_not_ready

                push ds
                push es
                pop ds
                assume ds:nothing

                ; Tell that we return info about current medium
;               mov [si].dpSpecFunc,GETDPSF_CURRENT

                mov al,[cs:conf + c_device_type]
                mov [si + dpDevType],al
                mov word [si + dpDevAttr],0   ; bit 0 Removable, bit 1 no change line

                mov ax,[cs:conf + c_BPB_heads]
                mul word [cs:conf + c_BPB_spt]              ; Implicit dx = 0
                mov cx,ax
                mov ax,[cs:conf + c_BPB_sectors]
                or ax,ax
                jnz gioctl_gp16bit
                  mov ax,[cs:conf + c_BPB_tsectorsl]   ; 32-bit disk
                  mov dx,[cs:conf + c_BPB_tsectorsh]
gioctl_gp16bit:
                div cx
                mov [si + dpCylinders],ax

                mov byte [si + dpMediaType],0  ; Normal media

                pop ds
                assume ds:d_seg

                mov di,si
                mov si,offset conf + c_BPB_bps    ; ds:si -> current BPB
                add di,7                        ; es:di -> Device Params BPB
                mov cx,25
                rep movsb               ; Copy BPB

                jmp cmd_ok
gioctl_getparam endp

%if GIOCTL_MEDIA
gioctl_setmedia proc near
                push es
                pop ds
                push cs
                pop es
                mov di,offset media_ID
                mov cx,MID_struc_size
                rep movsb
                jmp cmd_ok
gioctl_setmedia endp

gioctl_getmedia proc near
                mov di,si
                mov si,offset media_ID
                mov cx,MID_struc_size
                rep movsb
                jmp cmd_ok
gioctl_getmedia endp
%endif

cmd_getLogical  proc near
                lds si,[req_ptr]
                assume ds:nothing
                debug 'l',<word ptr [si].ldrUnit>
                mov byte [si + ldrUnit],0
to_cmd_ok:
                jmp cmd_ok
                assume ds:d_seg
cmd_getLogical  endp

cmd_setLogical  proc near
                lds si,[req_ptr]
                assume ds:nothing
                debug 'L',<word ptr [si].ldrUnit>
                cmp byte [si + ldrUnit],0
                je to_cmd_ok
                mov al,UNKNOWN_UNIT
                jmp cmd_error
                assume ds:d_seg
cmd_setLogical  endp

%endif ;if CAPABLE and C_GIOCTL

;**************************************************************************
;
;               LOCAL STACK
;
; The stack replaces part of the initialization code.
;**************************************************************************

%if STACKSIZE
;stack_check     dw STACK_CHECK_ID
db STACKSIZE / 8 dup ('STACK   ')       ; To avoid stack over init code
align 16                                ; Make use of space left on page
%endif

end_of_resident EQU offset $
; MARKS THE END OF RESIDENT PORTION OF DRIVER

;**************************************************************************
;**************************************************************************
;**************************************************************************
;**************************************************************************
;
;               INITIALIZATION TIME CODE
;
; This code and data is removed from the memory after driver is
; initialized.
;**************************************************************************

dos_drive       db 0                    ; DOS reported drive
def_drive       db 'C'                  ; Defined drive, where to install

install_ok      db FALSE                ; Installed OK
tryamisnumber:	db 0

main_config     dd      0               ; Pointer to main configuration table

FLAG_1ST        EQU 1                   ; First SRDISK driver
FLAG_FORCEDRIVE EQU 2                   ; Force drive letter (in def_drive)
FLAG_APPEND     EQU 4                   ; Append to other SRDISK driver
FLAG_APPENDED   EQU 8                   ; Appended to other SRDISK driver
FLAG_KNOWDRIVE  EQU 10h                 ; Drive letter known
FLAG_32BITS     EQU 20h                 ; Capable of 32 bit sector addressing
FLAG_GIOCTL     EQU 40h                 ; Supports GIOCTL
flags           db 0

;**************************************************************************
;
;                       prints macro
;
; This macro is used by initialization routines to display text.
; dx must point to the '$' terminated text about to be displayed.
;**************************************************************************
  
%macro prints 0
		mov	ah,9
                int     21h
%endmacro
  
%if DEBUGINIT

print_x         proc near               ; Print a dword in hex
print_lx:       push ax
                mov ax,dx
                call print_sx
                mov ah,2
                mov dl,':'
                int 21h
                pop ax
print_sx:                               ; Print a word in hex
                push ax
                mov al,ah
                call print_cx
                pop ax
%endif
print_cx:                               ; Print a byte in hex
                push ax
                shr al,1
                shr al,1
                shr al,1
                shr al,1
                call print_x1
                pop ax
print_x1:       and al,0Fh              ; Print a hex digit
                cmp al,10
                jae print_x2
                  add al,'0'
                  jmp print_x3
print_x2:       add al,'A'-10
print_x3:       mov ah,2
                push dx
                mov dl,al
                int 21h
                pop dx
                ret
print_x         endp

%if DEBUGINIT
idebugc  macro chr
        push ax
        mov al,[chr]
        call print_cx
        pop ax
endm

idebugs  macro wrd
        push ax
        mov ax,[wrd]
        call print_sx
        pop ax
endm

idebugl  macro high,low
        push ax
        push dx
        mov ax,[low]
        mov dx,[high]
        call print_lx
        mov ah,2
        mov dl,' '
        int 21h
        pop ax
        pop dx
endm

%else

%macro idebugc 1
%endmacro

%macro idebugs 1
%endmacro

%macro idebugl 2
%endmacro

%endif ; DEBUGINIT

  
;**************************************************************************
;
;                       INIT command
;
; Init command does the following:
;  - displays sign-on message
;  - checks DOS version. This driver is built in a way that requires
;    at least dos version 2.00. I'm not sure whether even that is enough.
;  - determine which drive we are by default
;  - read command line
;    - abort on syntax errors
;  - initialize multiplex interrupt
;  - do hooks to other SRDISK drivers (specified in command line)
;  - hook INT 19 bootstrap interrupt
;  - fills in the request header
;**************************************************************************
  
cmd_init        proc near
                ; Compulsory initializations
                mov word ptr [conf + c_disk_IO_seg],cs

                ; Sign on message
                mov dx,offset s_sign_on         ; "ReSizeable RAMdisk ver..."
                prints

                ; Find out the features about DOS and the drive letter
                call init_dos
                jnc cmd_init4
                  jmp cmd_init_err
cmd_init4:
                les si,[req_ptr]

                idebugl es,si
                idebugs <word ptr [es:si]>
                idebugs <word ptr [es:si+2]>
                idebugs <word ptr [es:si+16h]>

                mov al,[dos_drive]                ; Default install to drive
                mov [def_drive],al                ; .. reported by DOS

                ; If DOS has no support for 32 bit sector access, disable it
                test byte [flags],FLAG_32BITS
                jnz cmd_init2
                  and byte [conf + c_flags],~ C_32BITSEC
                  ;and drhdr_attr,NOT DHATTR_32BIT
cmd_init2:
                ; If DOS has no support for GIOCTL (diskcopy) disable it
                test byte [flags],FLAG_GIOCTL
                jnz cmd_init5
                  and word [drhdr_attr],~ DHATTR_GIOCTL
cmd_init5:
                call init_read_cmdline
                jc cmd_init_err

                call init_memory
                jc cmd_init_err

                call init_mplex
                jc cmd_init_err

                call init_hooks
                jc cmd_init_err
%if HOOKINT19
                call set_int19
%endif

                mov al,[def_drive]
                mov [conf + c_drive],al

                ; --------------- If appended
                test byte [flags],FLAG_APPENDED
                jz cmd_init7
                  mov [s_appdrive],al     ; Report append
                  mov dx,offset s_appended
                  prints
                  mov al, [i2D.multiplexnumber]
                  call print_cx
                  mov dx, s_end
                  prints

                  mov bx,offset ret_far ; Strategy and commands short
                  mov [drhdr_strategy],bx
                  mov [drhdr_commands],bx
                  mov word [drhdr_attr],DHATTR_CHARDEV
                  mov byte [drhdr_units],'$'   ; Name for this driver '$SRD'MEMORY

                  lds bx,[req_ptr]
                  assume ds:nothing
		  mov byte [bx + irUnits],1
		  mov word ptr [bx + irEndAddress],appended_eor
                  mov byte [cs:install_ok],TRUE
                  jmp cmd_init3
                assume ds:d_seg
cmd_init_err:   ; --------------- Error
                prints
cmd_init_abort:
                call deinit                     ; Remove hooks
                xor ax,ax
                lds bx,[req_ptr]
                assume ds:nothing
		mov [bx + irUnits],al		; Zero the number of drives
                mov word ptr [bx + irEndAddress],ax
                jmp cmd_init3
                assume ds:d_seg

cmd_init7:      ; --------------- Normal install
                mov al,[def_drive]
                mov [s_drive],al
                cmp al,'$'                      ; Is the drive number known?
                jne cmd_init8
                  mov word [s_drive],2020h    ; Don't tell drive
cmd_init8:
                mov dx,offset s_installed       ; Report install
                prints
                mov al, [i2D.multiplexnumber]
                call print_cx
                mov dx, s_end
                prints
                mov byte [install_ok],TRUE

                lds bx,[req_ptr]
                assume ds:nothing
		mov byte [bx + irUnits],1		; Save number of drives
                mov word ptr [bx + irEndAddress],end_of_resident
%if CAPABLE & C_GIOCTL
                test byte [cs:flags],FLAG_GIOCTL
                jnz cmd_init3
                  mov word ptr [bx + irEndAddress],noGIOCTL_eor
                  mov word [cs:commands.patchstack], noGIOCTL_eor
                  mov byte [cs:high_cmd],16
%endif
cmd_init3:      ; --------------- Return (whether failed or not)
		mov word ptr [bx + irEndAddress + 2],cs

		mov word ptr [bx + irParamAddress],offset pBPB
		mov word ptr [bx + irParamAddress + 2],cs

                ; --------------- After install operations
%if CAPABLE & C_DISK_IMAGE
                cmp byte [cs:install_ok],TRUE
                jne cmd_init6
                  call load_image
cmd_init6:
%endif
                jmp cmd_ok

                assume ds:d_seg
cmd_init        endp

;**************************************************************************
;
;               CHECK DOS VERSION AND CAPABILITIES
;
;**************************************************************************

init_dos        proc near
                mov al,'$'
                mov [dos_drive],al

                mov ax,4452h    ; DR-DOS?
                stc
                int 21h
                jc idos_notc
                cmp ax,dx
                jne idos_notc
                cmp ah,10h
                jne idos_notc   ; Not installed

                cmp al,67h      ; DR-DOS version 6.0 ?
                jne idos_notc   ; If not, treat it like MS-DOS

                  or byte [flags],FLAG_32BITS | FLAG_GIOCTL | FLAG_KNOWDRIVE
                  les si,[req_ptr]
                  mov al,[es:si + irDriveNumber]
                  add al,'A'
                  mov [dos_drive],al
                  jmp idos_x

idos_notc:      mov ah,30h
                int 21h         ; Get DOS version number

                xchg ah,al
                idebugs ax
                cmp ax,300h     ; Multiplex supported only after version 3.00
                jnb idos2
; Let's not be picky
;                cmp ax,700h
;                jb idos2
idos1:
                  mov dx,offset errs_eDOS       ; Invalid DOS version
                  stc
                  ret

i2Dinvalid:	mov dx, errs_i2D_invalid
		stc
		retn

idos2:
                cmp ax,314h	; DOS 3.20+ ?
                jb idos3
                  or byte [flags],FLAG_GIOCTL
idos3:
                cmp ax,31Fh     ; DOS 3.31+ ?
                jb idos4
                  or byte [flags],FLAG_32BITS
idos4:

	xor ax, ax
	mov es, ax
	les si, [es:2Dh * 4]
	cmp si, -1
	je i2Dinvalid
	mov ax, es
	test ax, ax
	jz i2Dinvalid

                les si,[req_ptr]
                cmp byte ptr [es:si],16h        ; Device number supported?
                jb idos_x                       ; No, make a guess
                  les si,[req_ptr]
                  mov al,[es:si + irDriveNumber]
                  add al,'A'
                  mov [dos_drive],al
                  or byte [flags],FLAG_KNOWDRIVE
idos_x:

;; !!!! The following does not work since the drhdr_next is not valid yet.
;
;                cmp dos_drive,'C'               ; Is invalid drive?
;                jb idosx1
;                cmp dos_drive,'Z'
;                jbe idosx2
;idosx1:
;                  les si,drhdr_next             ; Walk links up to count
;                  mov al,'A'                    ; block devices
;cntblk1:
;                  cmp si,0FFFFh
;                  je cntblk3
;                    test [es:si].dhAttributes,DHATTR_CHARDEV
;                    jnz cntblk2
;                      add al,[es:si].dhNameOrUnits
;cntblk2:
;                    les si,[es:si].dhLink
;                    jmp cntblk1
;cntblk3:
;                  mov dos_drive,al
;                  or flags,FLAG_KNOWDRIVE

idosx2:
                clc
                ret
init_dos        endp

  
;**************************************************************************
;
;               READ COMMAND LINE
;
; Return carry set if error
;**************************************************************************

init_read_cmdline proc near
                push ds

                les bx,[req_ptr]
		lds si,[es:bx + irParamAddress]	; Pointer to cmd line
                assume ds:nothing

irc1:           lodsb                           ; Skip over the driver name
                cmp al,9 ;tab
                je irc2
                cmp al,' '
                je irc2
                ja irc1
                jmp irc_eol
irc2:
irc_narg:       call irc_skip_space

                cmp al,' '                      ; Every ctrl character ends
                jb irc_eol

                cmp al,'/'
                jz irc_switch

		cmp al, 'a'
		jb @F
		cmp al, 'z'
		ja @F
                and al,11011111b                ; Make lowercase to uppercase
@@:
                cmp al,'A'
                jb irc_syntax
                cmp al,'`'
                ja irc_syntax

                cmp byte ptr [si],':'
                jne irc3
                inc si                          ; Skip ':'
irc3:           
                mov [cs:def_drive],al
                test byte [cs:flags],FLAG_FORCEDRIVE
                jnz irc_syntax
                or byte [cs:flags],FLAG_FORCEDRIVE
                jmp irc_narg

irc_syntax:     mov dx,offset errs_syntax
                stc
                pop ds
                ret

irc_switch:     lodsb
                and al,11011111b                ; Make lowercase to uppercase
                cmp al,'A'
		je .append
		cmp al, 'M'
		jne irc_syntax
		lodsb
		and al, ~20h
		cmp al, 'P'
		jne irc_syntax
		lodsb
		and al, ~20h
		cmp al, 'X'
		jne irc_syntax
.mpx:
		lodsb
		cmp al, 9
		je .mpx
		cmp al, 32
		je .mpx
		cmp al, '='
		je .mpx
		cmp al, ':'
		je .mpx
		call gethex
		jc irc_syntax
		mov dl, al
.loopmpx:
		lodsb
		call gethex
		jc .endmpx
		add dl, dl
		jc irc_syntax
		add dl, dl
		jc irc_syntax
		add dl, dl
		jc irc_syntax
		add dl, dl
		jc irc_syntax
		add dl, al
		jc irc_syntax
		jmp .loopmpx

.endmpx:
		mov byte [cs:tryamisnumber], dl
		dec si
		jmp irc_narg

.append:
                or byte [cs:flags],FLAG_APPEND
                jmp irc_narg

irc_eol:        clc
                pop ds
                ret
init_read_cmdline endp


gethex:
	cmp al, 'a'
	jb @F
	cmp al, 'z'
	ja @F
	sub al, 20h
@@:
	cmp al, '0'
	jb .err
	cmp al, '9'
	jbe .decit
	cmp al, 'A'
	jb .err
	cmp al, 'F'
	ja .err
.hexit:
	sub al, 'A' - ('9' + 1)
.decit:
	sub al, '0'		; NC
	retn

.err:
	stc
	retn


irc_skip_space  proc near
ircs1:          lodsb
                cmp al,' '
                je ircs1
                cmp al,9 ;tab
                je ircs1
                ret
irc_skip_space  endp

                assume ds:d_seg

  
;**************************************************************************
;
;                       Memory initialization
;
; Returns
;   carry set if error
;**************************************************************************
  
init_memory     proc near

%if METHOD EQ 1
; Get XMS driver API address

                push es
                mov ax,4300h
                int 2Fh                         ; Get XMS installed status
                cmp al,80h
                jne @@init1                       ; Jump if not installed
                mov ax,4310h
                int 2Fh                         ; Get XMS entry point
                jnc @@init2                       ; Jump if no error
@@init1:
                  mov dx,offset errs_noXMS      ; "No extended mem driver"
                  jmp @@init4
@@init2:
                mov word ptr [XMS_alloc + XMS_entry],bx
                mov word ptr [XMS_alloc + XMS_entry+2],es

                mov ah,0                        ; Verify XMS version
                call far [XMS_alloc + XMS_entry]
                cmp ax,200h
                jae @@init3
                  mov dx,offset errs_badXMS
                  jmp @@init4
@@init3:
                mov     word [XMS_alloc + XMS_handle],0  ; Zero handle for no handle

                clc
                jmp     @@init_ret
@@init4:
                stc
@@init_ret:
                pop     es
%endif

%if METHOD EQ 2
                push    es

                mov ax,3567h                    ; Compare device name
                int 21h
                mov di,0Ah
                mov si,offset EMM_name
                mov cx,8
                cld
                repz cmpsb
                jne @@init1

                mov ah,EMS_GET_STATUS           ; Get status of EMM
                int 67h
                or ah,ah
                jnz @@init1

                mov ah,EMS_FRAME_SEGMENT        ; Get page frame address
                int 67h
                mov [EMS_alloc + EMS_frame],bx
                or ah,ah
                jz @@init2
@@init1:
                  mov dx,offset errs_noEMS      ; "No expanded mem driver"
                  jmp @@init4
@@init2:
                mov ah,EMS_GET_VERSION          ; Verify EMS version
                int 67h
                or ah,ah
                jnz @@init1
                cmp al,40h
                jae @@init3
                  mov dx,offset errs_badEMS
                  jmp @@init4
@@init3:
                ; Try out the functions that are needed
                mov ah,EMS_ALLOCATE
                mov bx,1
                int 67h
                or ah,ah
                jnz @@init6
                mov [EMS_alloc + EMS_handle],dx

                mov ah,EMS_REALLOCATE_PAGES
                mov bx,2
                int 67h
                or ah,ah
                jnz @@init5

                mov ah,EMS_MOVE_MEM_REGION
                mov al,0
                mov si,offset EMS_alloc + EMS_cblk
                mov word [si + EMS_sizel],400h
                mov word [si + EMS_sizeh],0
                mov byte [si + EMS_stype],1            ; Source in EMS
                mov [si + EMS_shandle],dx
                mov word [si + EMS_soff],0
                mov word [si + EMS_spage],0
                mov byte [si + EMS_dtype],1            ; Destination in EMS
                mov [si + EMS_dhandle],dx
                mov word [si + EMS_doff],200h
                mov word [si + EMS_dpage],1
                int 67h
                or ah,ah
                jnz @@init5

                mov ah,EMS_DEALLOCATE
                mov dx,[EMS_alloc + EMS_handle]
                int 67h
                jz @@init7

@@init5:
                mov ah,EMS_DEALLOCATE
                mov dx,[EMS_alloc + EMS_handle]
                int 67h
@@init6:
                mov dx,offset errs_nofuncEMS
                jmp @@init4

@@init7:
                mov word [EMS_alloc + EMS_handle],-1 ; -1 handle for no handle

                clc
                jmp     @@init_ret
@@init4:
                stc
@@init_ret:
                pop     es
%endif

%if METHOD EQ 3
                push    es

                mov ax,3567h                    ; Compare device name
                int 21h
                mov di,0Ah
                mov si,offset EMM_name
                mov cx,8
                cld
                repz cmpsb
                jne @@init1

                mov ah,EMS_GET_STATUS           ; Get status of EMM
                int 67h
                or ah,ah
                jnz @@init1

                mov ah,EMS_FRAME_SEGMENT        ; Get page frame address
                int 67h
                mov [EMS_alloc + EMS_frame],bx
                or ah,ah
                jz @@init2
@@init1:
                  mov dx,offset errs_noEMS      ; "No expanded mem driver"
                  jmp @@init4
@@init2:
                mov ah,EMS_GET_VERSION          ; Verify EMS version
                int 67h
                or ah,ah
                jnz @@init1
                cmp al,40h
                jae @@init3
                  mov dx,offset errs_badEMS
                  jmp @@init4
@@init3:
                ; Try out the functions that are needed
                mov ah,EMS_ALLOCATE
                mov bx,1
                int 67h
                or ah,ah
                jnz @@init6
                mov [EMS_alloc + EMS_handle],dx

                mov ah,EMS_DEALLOCATE
                mov dx,[EMS_alloc + EMS_handle]
                int 67h
                jz @@init7

@@init6:
                mov dx,offset errs_nofuncEMS
                jmp @@init4

@@init7:
                mov word [EMS_alloc + EMS_handle],-1 ; -1 handle for no handle

                clc
                jmp     @@init_ret
@@init4:
                stc
@@init_ret:
                pop     es
%endif

                ret
init_memory     endp

;**************************************************************************
;
;               Multiplex service initialization
;
; Queries multiplex interrupt to find out if SRDISK device drivers are
; already installed. If yes, hook our ctrl0 into the primary header.
; if no, ctrl0 points to ourself. Always install our AMIS multiplexer.
;
; Return carry set if error.
;**************************************************************************

init_mplex      proc near
	xor ax, ax
.loop:
	mov al, 0
	int 2Dh			; installation check
	cmp al, -1
	je .check
.next:
	inc ah
	jnz .loop
.new:
	mov word [main_config], conf
	mov word [main_config + 2], ds
	or byte [flags], FLAG_1ST
	mov word [ctrl0 + 2], ds; offset already set (0)
	jmp .install

.check:
	mov es, dx
	mov cx, words(16)
	mov si, amis_sign
	repe cmpsw
	jne .next
.found:
	mov al, 10h
	int 2Dh			; get compatible version
	test al, al		; call supported ?
	jz .versionmismatch
	cmp cx, 200h + 10	; compatible to v2.10 ?
	jne .versionmismatch
	mov al, 20h
	int 2Dh			; get device driver header
	cmp al, 4		; call supported ?
	jne .versionmismatch
		; now dx:bx -> ctrl0
	mov es, dx		; => found ctrl0
	les bx, [es:bx]		; -> primary header
	mov word [ctrl0], bx
	mov word [ctrl0 + 2], es; refer to primary header
	test bx, bx
	jnz .versionmismatch
                cmp word [es:dr_ID],'SR'	; Is it SRDISK structure?	; NASM port swapped text literals
                jne .versionmismatch		; No, multiplex used elsewhere
                cmp byte [es:dr_ID+2],'D'
                jne .versionmismatch		; No, multiplex used elsewhere
                cmp byte [es:dr_v_format],V_FORMAT ; Proper version?
                jne .versionmismatch		; No, multiplex used elsewhere
                ; OK
                mov di,[es:dr_conf]
                mov word ptr [main_config],di
                mov word ptr [main_config+2],es

.install:
	mov ah, byte [cs:tryamisnumber]
	mov al, 0
	int 2Dh
	cmp al, 0
	je .foundfree
	xor ax, ax
.loopfree:
	mov al, 0
	int 2Dh
	cmp al, 0
	je .foundfree
	inc ah
	jnz .loopfree
	mov dx, errs_i2D_no_free
	jmp .error

.foundfree:
	mov byte [i2D.multiplexnumber], ah
	mov ax, 352Dh
	int 21h
	mov word [i2D.next + 2], es
	mov word [i2D.next], bx
	mov dx, i2D
	mov ax, 252Dh
	int 21h

	clc
	jmp @F

.versionmismatch:
                mov dx,offset errs_ml_version
.error:
		stc
@@:
		push ds
		pop es
		retn
init_mplex      endp


;**************************************************************************
;
;               INIT HOOKS to previous SRDISK drivers
;
; Append this driver into the list of installed SRDISK drivers
; Return carry set if error
;**************************************************************************

init_hooks      proc near
                test byte [flags],FLAG_1ST     ; If we are the first driver
                jnz ihxok               ;  no hooks are to be done

                les di,[main_config]      ; es:di point to a drive config
                mov al,[def_drive]        ; al is the drive to search
                cmp al,'$'              ; Is drive letter unknown?
                je ih_nodrive           ; Yes, do not check drive letter

                test byte [flags],FLAG_APPEND          ; If we append
                jz ih_find_drive
                test byte [flags],FLAG_FORCEDRIVE      ; but not specify drive
                jnz ih_find_drive
ih_nodrive:       mov al,-1                     ; make sure drive not found
ih_find_drive:
ih1:            cmp [es:di + c_drive],al          ; Is it the same drive?
                je ih_append
ih2:            test word ptr [es:di + c_next_drive],-1 ; Is there next drive
                jz ih_newdrive                  ; No (valid segment is nonzero)
                mov es,[es:di + c_next_drive]     ; Yes, find the next drive
                mov di,[es:dr_conf]
                jmp ih1

ih_append_new:  ; Append this driver into previously installed drive
                mov al,[es:di + c_drive]          ; Find the drive letter
                mov [def_drive],al
                mov [conf + c_drive],al

ih_append:      ; Append this driver into specified drive
                test byte [es:di + c_flags],C_APPENDED ; Append allowed?
                jz ih_appendfail                ; No, fail

                test word ptr [es:di + c_next],-1 ; Is there next driver
                jz ih_a1                        ; No, append here
                mov es,[es:di + c_next]           ; Yes, find the next drive
                mov di,[es:dr_conf]
                jmp ih_append

ih_appendfail:  mov al,[dos_drive]
                mov [def_drive],al
                mov dx,offset errs_noappend
                stc
                ret

ihxok:          clc
ihx:            ret

ih_a1:          mov [es:di + c_next],ds
                or byte [flags],FLAG_APPENDED  ; Remember to free extra memory
%if DEBUGGING
                mov ax,[es:d_col]         ; Debug data display little left from
                sub ax,16               ;  main data display
                mov [d_col],ax
%endif ;DEBUGGING
                jmp ihxok

ih_newdrive:    test byte [flags],FLAG_APPEND
                jnz ih_append_new
                ; This driver must be placed at the tail of list of
                ; SRDISK drivers
                mov [es:di + c_next_drive],ds

                jmp ihxok
init_hooks      endp


;**************************************************************************
;
;                       INT 19 hooking
;
; INT 19 is the bootstrap loader interrupt, which is invoked when user
; presses Ctrl-Alt-Del. We must hook it in order to release the
; extended memory allocated for RAM disk.
;**************************************************************************
  
%if HOOKINT19

set_int19       proc near
		push ax
		push dx
		push bx
		push es

                mov ax,3519h
                int 21h                         ; Get old int 19 handler
                mov [old_int19 + 0],bx
                mov [old_int19 + 2],es
                mov dx,offset int_19_entry
		mov ax,2519h
                int 21h                         ; Set new int 19 handler

		pop es
		pop bx
		pop dx
		pop ax
		retn
set_int19       endp
  
%endif

;**************************************************************************
;
;               Deinitialization in case of aborted install
;
;**************************************************************************

deinit          proc near
%if HOOKINT19
                mov ax,[old_int19 + 2]
                or ax,[old_int19 + 0]
                jz di_noint19

                push ds
                lds dx, [old_int19]
                mov ax,2519h
                int 21h                         ; Set old int 19 handler
                pop ds
di_noint19:
%endif

;**************************************************************************
;
;                       Memory deinitialization
;
;**************************************************************************

%if METHOD EQ 1
                cmp word [XMS_alloc + XMS_handle],0
                je @@done

                mov dx,[XMS_alloc + XMS_handle]
                mov ah,0Ah
                call far [XMS_alloc + XMS_entry]        ; Free XMS memory
                mov word [XMS_alloc + XMS_handle],0
@@done:
%endif

%if METHOD EQ 2
                cmp word [EMS_alloc + EMS_handle],-1
                je @@done

                mov dx,[EMS_alloc + EMS_handle]
                mov ah,EMS_DEALLOCATE
                int 67h                         ; Free EMS memory
                mov word [EMS_alloc + EMS_handle],-1
@@done:
%endif

%if METHOD EQ 3
                cmp word [EMS_alloc + EMS_handle],-1
                je @@done

                mov dx,[EMS_alloc + EMS_handle]
                mov ah,EMS_DEALLOCATE
                int 67h                         ; Free EMS memory
                mov word [EMS_alloc + EMS_handle],-1
@@done:
%endif

old_multiplex equ i2D.next
                mov ax,word ptr [old_multiplex]
                or ax,word ptr [old_multiplex+2]
                jz no_mplex

                push ds
                mov dx,word ptr [old_multiplex]
                mov ds,word ptr [old_multiplex+2]
                mov ax,252Dh
                int 21h                         ; Set old multiplex handler
                pop ds
no_mplex:

                ret
deinit          endp


;**************************************************************************
;
;               Loading of included disk image
;
;**************************************************************************

%if CAPABLE & C_DISK_IMAGE
load_image      proc near
                test byte [flags],FLAG_APPENDED
                jz li1
                ; If appended, must copy parameters to the first driver

li1:
                ; !!!! Allocate memory
                ; !!!! Move data to disk
                mov dx,offset errs_load_fail
                prints
                ret
load_image      endp

errs_load_fail  db 'RAMDisk: Loaded disk image', 0Dh, 0Ah, '$'
s_image_loaded  db 'RAMDisk: Loaded disk image', 0Dh, 0Ah, '$'

%endif

;**************************************************************************
;
;                       Initialization time data
;
;**************************************************************************

%if METHOD EQ 1
errs_noXMS      db 'RAMDisk: Extended Memory Manager not present.'
                db 0Dh, 0Ah, '$'

errs_badXMS     db 'RAMDisk: Invalid XMS version. XMS 2.0 or above needed.'
                db 0Dh, 0Ah, '$'
%endif

%if METHOD EQ 2
EMM_name        db 'EMMXXXX0'

errs_noEMS      db 'RAMDisk: Expanded Memory Manager not present.'
                db 0Dh, 0Ah, '$'

errs_badEMS     db 'RAMDisk: Invalid EMS version. EMS 4.0 needed.'
                db 0Dh, 0Ah, '$'

errs_nofuncEMS  db 'RAMDisk: EMS driver does not support needed functions'
                db ' or no memory available.'
                db 0Dh, 0Ah, '$'
%endif

%if METHOD EQ 3
EMM_name        db 'EMMXXXX0'

errs_noEMS      db 'RAMDisk: Expanded Memory Manager not present.'
                db 0Dh, 0Ah, '$'

errs_badEMS     db 'RAMDisk: Invalid EMS version. EMS 4.0 needed.'
                db 0Dh, 0Ah, '$'

errs_nofuncEMS  db 'RAMDisk: EMS driver does not support needed functions'
                db ' or no memory available.'
                db 0Dh, 0Ah, '$'
%endif

;**************************************************************************
;
;                       Initialization strings
;
;**************************************************************************

errs_eDOS       db 'RAMDisk: Incorrect DOS version.'
                db 0Dh, 0Ah, '$'

errs_i2D_invalid:	ascic "RAMDisk: Multiplex interrupt 2Dh invalid.",13,10

errs_i2D_no_free:	ascic "RAMDisk: No free int 2Dh multiplex number.",13,10

errs_ml_version db 'RAMDisk: Driver of different version already '
                db 'installed.'
                db 0Dh, 0Ah, '$'

errs_noappend   db 'RAMDisk: Can not append to previously installed driver.'
                db 0Dh, 0Ah, '$'

errs_syntax     db 'RAMDisk: Syntax error', 0Dh, 0Ah, 0Dh, 0Ah
                db 'Syntax: SRDISK.SYS [d:] [/A] [/MPX=hh]', 0Dh, 0Ah, 0Dh, 0Ah
                db ' d:', 9, 'Drive into which to append or the drive letter'
                db 0Dh, 0Ah
                db 9, 'of this device if DOS does not report it to it '
                db 'correctly.'
                db 0Dh, 0Ah
                db ' /A', 9, 'Append this driver to previous SRDISK '
                db 'driver.'
                db 0Dh, 0Ah
                db " /MPX=",9,"Set multiplex number (hexadecimal) to try first."
                db 0Dh, 0Ah, '$'

s_sign_on       db 0Dh, 0Ah, 'ReSizeable RAMDisk '
                db '(', MEMORY_STR, ')'
                db ' version ', SRD_VERSION, '. '	; NASM port swapped text literals
                db 'Copyright (c) 2005 Marko Kohtala.'
                db 0Dh, 0Ah, '$'

s_installed     db 'Installed RAMDrive '
s_drive         db "C: on multiplex ",36
  
s_appended      db 'Appended to RAMDrive '
s_appdrive      db "C: on multiplex ",36

s_end:		db "h",13,10,36


;**************************************************************************
;
;                       A note for binary debuggers
;
;**************************************************************************

db 0Dh, 0Ah, "Copyright (C) 1992-1996,2005 Marko Kohtala. "
db 0Dh, 0Ah, "Contact at 'kohtala@users.sourceforge.net'."
db 0Dh, 0Ah

; (no prior section) ; d_seg           ends
                end

%ifidni __OUTPUT_FORMAT__, obj
section STACK stack
	resb 512			; to silence linker warning
%endif
