;Version 2.1   1982    Mark S Zachmann
;
;	This program lets you totally remap the
; disks on MS-DOS. You can map each side of a logical 
; drive to any other side or to memory.
;
;	For example, a usual configuration would be
; map drive 0, sides 0,1 to drive 0 sides 0,1 (no change)
;     drive 1, sides 0,1 to drive 1 sides 0,1    "
;     drive 2, side 0 to   segment  4000h
;     drive 2, side 1 to   segment  6800h
;
;  this will let you use 256K for the MS-DOS system, and another
; 320K of memory for a double-sided RAM disk, if you have a 512K 
; board installed. ( for 576K total )

;**********************************************
; to alter this for your system:
;   change the equates at locations 'DISKPTR'
;    they come in order (0/0, 1/0, 0/1, 1/1, 0/2, 1/2, 0/3, ...
;	and each entry says which to map to:
;
;	0h = side 0/disk 0
;     100h = side 1/disk 0
;       1h = side 0/disk 1
;     101h = side 1/disk 1
;       ...etc
;     anything bigger than f00h is treated as a location in RAM,
;    thus
;     4000h = segment beginning at 4000:0000h
;     6800h = segment beginning at 6800:0000h
;	etc
;*********************************************
; also, 0FFFFh = return an error if this side is selected.

; for my default setup: (disk A,B normal, C RAMdisk, ...)
;	Note that the dip switches inside must then be set to
; indicate 3 disk drives and 256K of memory. All disk operations
; are transparent to the user.
;	this is for two reasons:
;(1) MS-DOS must feel that any RAMdisk memory does not exist
;(2) MS-DOS needs to know how many drives (real or otherwise)
;	there are
; Note that the RAM disks must be FORMATted before use, unless
; you use DISKCOPY to send data to them.
;
;
;  SYNTAX:

;	A>ramdisk		creates a ramdisk and writes F6 to each byte
;				to eliminate possible parity errors on read
;
;	A>ramdisk old		creates a ramdisk, but does not overwrite
;				old data. used if you had to reset
;				(ctrl-alt-del) for some reason


TITLE Double-Sided Diskette Support
SUBTTL


BASE	equ	0h	;Base of memory
DiskInt	equ	4Ch	;Offset of int
DISKETTE_STATUS equ	0441h
DISK_POINTER	equ	078h
bufr	equ	80h	; command line parm address


;first the macro library must be in here

	IF1
Include Interupt.MAC
	ENDIF

;first macro ----

	Prolog	DOUBLE

  ASSUME DS:DOUBLE,CS:DOUBLE
;----------------------------------------
;----------------------------------------
;----------------------------------------

SUBTTL	Diskette I/O Call Entry Point

COMMENT *   Actual Entry point 
   On entry:
	DL = logical disk number (0-n)
	DH = 0 (side-1)
   On call to disk I/O:
	DL = physical disk number
	DH = side number (0 or 1)
	     DH=0 or 1 (1 if DL was odd)

   For other registers...
	See Reference Manual p.A-32

IF reference is to memory_disk
  {  Case:
	RESET - pass to usual disk reset
	STATUS - "	"	"
	READ   - transfer buffer to memory
	WRITE  - transfer memory to buffer
	VERIFY  - do nothing (just like BIOS)
	FORMAT  - fill sectors(s) with [DISK_BASE+8]

   Always return successful completion code.
	unless signaled otherwise by 0xFFFF assegment
  }

 Upon return, all registers except AX unchanged.
	Memory references also set
	DISKETTE_STATUS to 0.

	*

;---------------------------------------------
;
;	Actual diskette I/O filter routine
;
;---------------------------------------------
Main	proc	far

ENTRY:	STI	;Allow interrupts
	push	DX	;So we can reset it later
	cmp	AH,1	;Is it disk independent?
	jle	USUAL	;Yes, do what is usual.

; Now figure out which disk is involved.
;	by table lookup. No validity check here.
; The table will return a DX (DH - side, DL - drive)
;	value for use or > 3FFh which implies a memory
;	reference, in which case the two bytes
;	are a starting segment number.
; ** Special Case:
;	if address is FFFF hex (impossible, ROM is there)
;	we return an error code, so you can use 160K ramdisk
;	effectively as single sided disk
;	.Most DOS routines try to read side 2, hence this kludge
;	for a 160K ramdisk

	XCHG	BX,DX	;Need BX for indexing
	SHL	BL,1	;*2 for word offset
	add	BL,BH	;side parameter
	shl	BL,1
	xor	BH,BH
	MOV	BX,CS:[BX+offset DISKPTR]	;address/map
	xchg	BX,DX	;back again
	cmp	DX,0FFFFh	;kludge ?
	je	Doerror		;generate error, for opposite side
				;of 160K ramdisk
	cmp	DX,03FFh	;is memory ?
	ja	short MEMdisk	;yes

;----------------------------------------------
;
;Now call the operating system
;	because call requires disk activity
;
;-----------------------------------------------

USUAL:	pushf		; because OS does IRET
			;which wastes a stack item.
	call	cs:disket	;call BIOS

;Return to calling routine

ToDOS:	pop	dx	;old DX value, must be intact
	ret	2	;keep flags intact
Main	endp

;----------------------------------------------
;
;	Use memory for disk
; On entry:	DX -  starting SEGMENT
;		other registers as in BIOS call
;		old DX on top of stack
;
; On exit:
;		All registers returned 
;	to normal. AX set to status, and
;	DISKETTE_STATUS set to 0.
;	Also CY = 0, unless segment was FFFFh
;
;
;-----------------------------------------------

Doerror	proc	near		
	clc
	cmp	AH,5		;format operation?
	mov	AH,0
	je	ToDos		;if format, then say OK
				;error routine for single
	mov	AX,400h		;sided operation of ramdisk
	stc
	jmp	short ToDOS	;no need to do the push/pop stuff
Doerror	endp


MEMdisk	proc	near		;usual memory disk, can't return error
; First calculate the starting address
;	DX+CH*100h+CL*20h
	push	DI
	push	SI
	push	DS
	push	ES
	push	CX
	push	BX
	push	AX
	CLD		;always increment string moves

; if parms change then leave reg's alone
	xor	AX,AX
	mov	AL,CL	;sector
	dec	AX	;relative to 0
	mov	CL,5
	SHL	AX,CL	;*20h
	add	AH,CH	;+track*100h
	add	AX,DX	;+segment
	mov	DS,AX	;now DS contains buffer addr

	pop	AX	;get AX back, AL has count
	push	AX
	xor	CX,CX	;how many sectors
	mov	CH,AL	;*256 for words to move

; set up the parameters for DS:SI -> ES:DI
;	i.e. a read operation
	xor	SI,SI	; ->0
	mov	DI,BX	; was ES:BX

; now do the operation

	cmp	AH,3
	jb	short READ
	je	short WRITE
	cmp	AH,5
	jb	short VERIF
	je	short FORMAT
	STC		;unknown, set error flag

;----------------------------------
;
; Return to DOS by setting status
;	to 0 (all OK) and then pop'ing
;	registers (leave flags alone)
;
;-------------------------------------

Memret	label	near
	mov	AX,Base
	mov	DS,AX
	mov	BX,DISKETTE_STATUS
	mov	BYTE PTR [BX],0
	pop	AX
	pop	BX
	pop	CX
	pop	ES
	pop	DS
	pop	SI
	pop	DI
	mov	AH,0	;Status NEC no problems!
	CLC		;impossible to get error
	jmp	short ToDOS
MEMdisk	endp


;--------------------------------------
;
;   Memory I/O routines.
; On Entry:
;	ES:DI = DMA address for transfer
;	DS:SI - buffer address
;	 CX   = number of words to transfer
;
;  On Exit:
;	CY = 0
;
;------------------------------------------

;----------------------------------
;
; Memory read
;
;----------------------------------

READ	proc	near
	rep movsw
	jmp	short MEMret
READ	endp

;-----------------------------------------
;
; Memory write
;
;  On entry as in read
;
;------------------------------------------

WRITE	proc	near
	call	REVERS	;backwards move
	rep movsw
	jmp	short MEMret
WRITE	endp

;-----------------------------------------
;
; Memory Verify
;	note that verify does nothing
;	if you look at reference manual
;	p.A-34 it is clear that
;	verify only tries to read a track!
;	which must work here
;
;-----------------------------------------

VERIF	proc	near
	jmp	short MEMret
VERIF	endp

;---------------------------------------
;
; Memory Format
;
;-----------------------------------------

FORMAT	proc	near
	call	REVERS	;source backwards
	mov	AX,BASE
	mov	DS,AX
	mov	BX,DISK_POINTER
	lds	BX,dword ptr [BX]
	mov	AL,BYTE PTR [BX+8]	; Format char
	mov	AH,AL	;Dup
	rep	stosw
	jmp	short MEMret
FORMAT	endp


;-----------------------------------
;
;	REVERS
; Change Direction, i.e.
;	XCHG	DS:SI,ES:DI
;
;------------------------------------

REVERS	proc	near
	mov	AX,DS
	mov	BX,ES
	mov	ES,AX
	mov	DS,BX
	xchg	DI,SI
	ret
REVERS	endp

;--------------------------------------
;
; Permanent storage area
;
;--------------------------------------

 
disket	LABEL	DWORD
	DW	?
	DW	?
;you can patch this (before or after assembly)
;or use the 7E and 7F system calls to set this
;on the fly
DISKPTR	label	WORD
	dw	0,100h,1,101h	;my usual setup
	dw	4000h,6800h	;C disk is all memory
	dw	0FFFFh, 0FFFFh		;D disk is error
	dw	12 dup(0)	;extra

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;!!
;!! everything after this is temporary
;!!
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


;---------------------------------------------
;   This 'subroutine' sets up the operating
;	system so that the ENTRY routine
;	can map logical disks into physical
;	disks and memory.
;
;   Store the old interrupt vector 13h
;	at an indirect call location 'disket'.
;	Then replace the vector
;	with the user entry point (ENTRY).
;   This has the effect of leaving the
;	full user routine in memory until
;	there is a warm start.
;
;   On entry:
;	DS = origin of program segment
;  Effect on registers is irrelevant.
;
;------------------------------------------


; macro ---

	Epilog	013h,Disket,DOUBLE

;---------------------------------
;
; Fill Memory Disk areas with zero's
;	for parity
;
;-----------------------------------

;			 if Old then do not fill
	mov	AX,CS:zzarg	; first word at 80h
	cmp	AL,'O'
	je	Nofl
	cmp	AL,'o'		;u/l case
	je	Nofl
;
; must not have been an O
;
	int	12h	;memory size(K) -> AX
	mov	CL,6	;*64
	shl	AX,CL	;gives segment #
	CLD		;upwards
	mov	BX,AX	;for loop convenience
	mov	DX,0800H	;32K increment
FILLUP:	mov	ES,BX
	mov	AX,0F6F6h
	xor	DI,DI	;from 0 base
	mov	CX,4000h ;words/32k
	rep stosw
	mov	BX,ES
	add	BX,DX
	cmp	BH,0B0h	;video yet ?
	jb	FILLUP

;------------------------------------
;
; Configure the disks
;
;---------------------------------------

Nofl:	jmp	CONFIG
HIMSG:	db	'Disk reconfiguration program',13,10
	db	' 1982, MSZachmann',13,10,'$'

; macro ---

	Finish	DOUBLE,250

	end

	STC		;unknown