
	 TITLE 'MEMORY DISK ADDON MODULE VERSION 2.0 OF 2/9/83'

;******************************************************************************
;	        MICRO RESOURCES CP/M MEMORY DISK ADDON MODULE
;******************************************************************************
;
;
;	THIS PROGRAM IS A SMALL TRANSIENT PROGRAM BASED BIOS SUBSTITUTE
;	THAT ALLOWS FILE TRANSFER UTILITY BETWEEN THE NORMAL CP/M SYSTEM
;	DISKS AND A RESIDENT "MEMORY DISK". THIS TRANSIENT PROGRAM IS 
;	SETUP FOR ANY VERSION 2.2 CP/M RUNNING IN AN S100 COMPUTER WITH
;	THE CCS 2065 BANK SWUTCHED DYNAMIC RAM CARDS.
;
;	THIS PROGRAM ALLOWS FOR A MEMORY RESIDENT FLOPPY DISK TO BE LOCATED
;	IN A SEVERAL CCS BANK SWITCHED BOARDS. THE PROGRAM SUPPORTS UP TO
;	EIGHT BOARDS, ONE AS THE MASTER BANK AND UP TO SEVEN AS THE DISK
;	DATA STORAGE AREA. THE BANK SWITCHING IS DONE AT THE 48K BYTE ADDRESS
;	BOUNDARY ABOVE WHICH ONLY ONE CARD HAS A 16K BANK ENABLED. BELOW THIS
;	EACH BOARD CONTAINS 48K BYTES THAT IS BANK SWITCHED IN PLACE OF THE 
;	TPA FOR THE MEMORY DISK STORAGE SPACE. THE FORMAT OF THE CP/M 
;	SYSTEM ALLOWS FOR A TOTAL OF 64 DIRECTORY ENTRIES AND USES A 1K
;	BYTE BLOCK ALLOCATION SIZE. THIS ALLOWS MAXIMUM FLEXIBILITY OF
;	SMALL DIRECTORY SIZE AND SMALL BLOCK SIZE TO CONSERVE MEMORY SPACE 
;	ON SMALL FILES.
;
;	PROGRAM ACCESS TO THE RAM DRIVE IS DONE IN 128 BYTE SECTORS
;	THAT ARE DIRECTLY TRANSFERRED FROM/TO THE USER TRANSIENT PROGRAM.
;	THE INITIAL LOADING OF THIS SOFTWARE SWAPS OUT THE NORMAL CP/M DISK 
;	I/O ENTRY POINTS TO THE BIOS WITH A NEW SET OF ENTRY POINTS TO THIS 
;	MODULE. THIS MODULE THEN CHECKS ALL SELECT DISK ACCESSES FOR LOGICAL 
;	UNIT M: AND WILL AND  WILL STEER I/O REQUESTS FOR THIS DRIVE THROUGH 
;	DRIVERS CONTAINED WITHIN THIS PROGRAM. NOTE THAT THIS PROGRAM STILL 
;	DEPENDS UPON THE HOST CP/M SYSTEM BDOS AS THE FILE INTERFACE MEDIUM.
;
;	OPERATION OF THE PROGRAM IS DONE TO MOVE THE MODULE UP TO A WORKSPACE
;	BELOW THE MEMORY RESIDENT CCP. THE WARM BOOT VECTOR AT THE SYSTEM
;	WARM BOOT ENTRY POINT IS SWAPPED TO A NEW ENTRY POINT WITHIN THE
;	RELOCATED I/O MODULE. THE NEW WARMBOOT FUNCTION SIMPLY RE-ENTERS
;	THE ALREADY PRESENT CCP FOR FURTHER OPERATOR COMMAND PROCESSING.
;	THE BDOS ENTRY VECTOR IS ALSO MODIFIED TO PERMIT THE DYNAMIC
;	MODIFICATION OF THE USER PROGRAM AREA SIZE SUCH THAT THE CCP AND
;	THE RELOCATED I/O MODULE DO NOT GET OVERLAYED BY THE TRANSIENT 
;	PROGRAM AREA'S BUFFER SPACES. THE UTILITY, WHEN LOADED PERFORMS
;	A CHECK TO VERIFY WHETHER A RELOCATED MODULE IS ALREADY PRESENT 
;	IN MEMORY. THE ALREADY PRESENT CHECK IS DONE VIA A SPECIALLY DEFINED
;	BDOS CALL THAT REQUESTS THE OPENING OF A FILE WITH THE SPECIALLY
;	DEFINED NAME SEQUENCE OF "A,,,,,,,,.,,," AS THE FILE NAME WHERE (A) 
;	IS A REQUEST NUMBER FOR PRESENCE CHECKING. AS THIS CHARACTER SEQUENCE 
;	IS AN ILLEGAL FILE NAME SEQUENCE, THE CHECK PROGRAM WILL TRAP THE NAME
;	AND RETURN A ZERO BYTE IN THE (A) REGISTER IF THE MODULE IS PRESENT.
;	IF THE ADDRESS BYTE IN THE FIRST BYTE OF THE FCB IS NOT RECOGNIZED,
;	THEN THE MODULE PASSES THE OPEN FILE REQUEST ON TO THE NEXT HIGHER
;	LEVEL BDOS CALL. IN ANY CASE THE NON PRESENCE OF A FILE BY THE NAME
;	OF "A,,,,,,,.,,," IS VIRTUALLY ASSURED TO CAUSE THE BDOS TO RETURN
;	A NOT FOUND "0FFH" ERROR CODE IN THE (A) REGISTER. THIS WOULD INDICATE
;	THE ABSENSE OF THE MODULE BEING CHECKED FOR.
;
;
;	THIS MEMORY DISK DRIVER PACKAGE
;	PROGRAM IS COPYRIGHT PROTECTED BY:
;
;	COPYRIGHT (C) 1983		
;
;	MICRO RESOURCES
;	2468 HANSEN COURT 
;	SIMI VALLEY, CALIFORNIA 93065 
;	(805) 527-7922
;
;*****************************************************************************
;
;
;
;DEFINE TRUE AND FALSE ASSEMBLY PARAMETERS
;
TRUE	EQU	-1		;DEFINE TRUE
FALSE	EQU	NOT TRUE	;DEFINE FALSE
;
;
HARDSIZ EQU	FALSE		;TRUE IF HARD SIZE CODING
BDTRKS	EQU	8		;NUMBER OF 48K BANKS (HARD CODED)
BIGMEM	EQU	FALSE		;TRUE IF THE AVAILABLE RAM ABOVE THE
				;..TOP OF THE BANK SWITCH AREA CAN
				;..ACCOMODATE THIS MEMORY DISK ADDON
				;..MODULE. IF NOT, THEN MEMORY ABOVE THE 
				;..BIOS MUST BE MADE AVAILABLE AS SPECIFIED
				;..BY THE "IPPROG" EQUATES BELOW. THE 
				;..NUMBER OF BYTES NEEDED BY THE RESIDENT
				;..DRIVER IS 256 BYTES.
MAXTRK	EQU	8*3		;MAXIMUM NUMBER OF POSSIBLE 16K TRACKS
				;..ON 8 48K BOARDS
DIRTRK	EQU	3		;TRACK NUMBER FOR DIRECTORY START
MAXSEC	EQU	128		;NUMBER OF 128 BYTE SECTORS IN A 
				;..16K TRACK
;
;
;SOME ASCII CHARACTERS
;
CR	EQU	0DH		;CARRIAGE RETURN
LF	EQU	0AH		;LINE FEED
;
;
;DEFINE RAMDISK MODULE SELECT ADDRESS AS SPECIAL VALUE
;
MODADDR	EQU	0CCH		;ADDRESS OF THIS MODULE
;				;..FOR CALIFORNIA COMPUTERS (HA HA)
;
;CP/M BDOS INTERFACE EQUATES
;
	ASEG
BOOT	EQU	0000H		;FIXED BOOT ADDRESS
BDOS	EQU	0005H		;FIXED BDOS ADDRESS
DEFFCB	EQU	005CH		;DEFAULT FCB LOCATION
DEFBUF	EQU	0080H		;DEFAULT SYSTEM BUFFER
RESET	EQU	13		;RESET DISK SYSTEM
OPEN	EQU	15		;OPEN FILE
STDMA	EQU	26		;SET DMA ADDRESS
;
;
;CCS 2065 RAM BOARD ACCESS ADDRESS EQUATES
;
BANKSEL	EQU	040H		;CCS FACTORY DEFAULT BANK SELECT PORT
;
	IF	NOT BIGMEM
IPPROG	EQU	0F500H		;LOCATION TO BLOCK MOVE PROGRAM TO FOR
				;..RESIDENT MEMORY I/O PROGRAM ABOVE BIOS
IPBUF	EQU	IPPROG+080H	;RESIDENT MEMORY PAGE DISK BUFFER
	ENDIF

	IF	BIGMEM
IPPROG	EQU	RWXFR		;IF PLENTY OF MEMORY LET THE DRIVER
				;..RESIDE AT TOP OF ADDON MODULE
	ENDIF
;
BANKTOP	EQU	0C000H		;TOP ADDRESS +1 FOR THE 48K BANK AREAS
;
;
;DEFINE MACRO LIBRARY FOR Z80 OPCODES
;
	MACLIB	Z80		;DRI Z80 OPCODE MACROS
;
;
;DEFINE MACRO LIBRARY FOR DEFINING THE DISK
;
	MACLIB	DISKDEF		;STD DRI PACKAGE
;
;
;CP/M 2.2 TO HOST DISK CONSTANTS
;
BLKSIZ	EQU	1024		;CP/M ALLOCATION SIZE
HSTSIZ	EQU	128		;HOST DISK SECTOR SIZE
;
SECSIZ	EQU	128		;NUMBER OF BYTES IN DISK RECORD
;
;
;*****************************************************************************
;
;
	CSEG			;SET ORIGIN TO ZERO FOR RELOCATABLE
				;ASSEMBLY BY RMAC
;
;
;
;FIRST TIME START UP ENTRY POINT FOR THE SINGLE DENSITY
;AUTO RELOCATING I/O MODULE. ENTRY HERE ASSURES PRESENSE OF
;CP/M 2.2. 
;
	JMP	CHKPRES		;GO CHECK IF A MODULE OF 
				;SAME FUNCTION ADDRESS IS PRESENT
				;IN SYSTEM
;
;
;DUMMY SPACE OFFSET TO PUT NEW BDOS ENTRY CODE AT A 06H PAGE
;BOUNDARY OFFSET TO PERMIT DIGITAL RESEARCH DDT AND XSUB TO 
;OPERATE WITHOUT A GLITCH
;
	DS	3
;
;
;NEWLY DEFINED ENTRY POINT INTO THIS MODULE FOR THE BDOS 
;CALL VECTOR FUNCTION.
;
L5ENT:
	JMP	BDOS$SCAN	;TO ENTRY POINT TO SCAN BDOS
				;FUNCTION CALLS
;
;
;SUBSTITUTE BDOS ENTRY POINT. EXECUTION ADDRESS IS PLACED HERE 
;FROM LOCATION 6 & 7 BY THE START UP MODULE PROVIDED THIS
;MODULE IS DETERMINED TO NOT ALREADY BE IN MEMORY.
;
TO$BDOS:
	JMP	$-$		;ENTER ADDRESS AT STARTUP
;
;
;START UP CHECK ROUTINE TO SEE IF THIS SOFTWARE WAS ALREADY 
;LOADED BY A PREVIOUS OPERATOR COMMAND.
;
CHKPRES:
	MVI	C,OPEN		;ATTEMPT TO OPEN FILE "A,,,,,,,.,,,"
	LXI	D,CHKFCB	;POINT AT THE CHECK FCB
	CALL	BDOS		;CALL NORMAL BDOS ADDRESS
	ORA	A
	JNZ	NOT$PRES	;NON ZERO RETURN MODULE IS NOT
				;..PRESENT
	LXI	D,PRESMSG	;POINT TO PRESENT MESSAGE
	MVI	C,9		;PRINT FUNCTION CODE
	CALL	BDOS		;PRINT ALREADY PRESENT MESSAGE
	RET			;SIMPLE RETURN TO THE CCP
;
CHKFCB:
	DB	0,MODADDR,',,,,,,,,,,',0,0,0,0
	DS	16
	DB	0
;
PRESMSG:
	DB	CR,LF,'MICRO RESOURCES Memory Disk'
	DB	CR,LF,'Access Already Active','$'
;
;
;HERE IF THIS RELOCATED MODULE IS NOT PRESENT IN MEMORY
;
NOT$PRES:
	POP	H		;COMPUTE CCP RE-ENTRY POINT
	PUSH	H
	LXI	D,075CH		;NEGATIVE OFFSET TO CCP ENTRY
				;WITH NO AUTO LOAD
	MOV	A,L
	SUB	E
	MOV	L,A
	MOV	A,H
	SBB	D
	MOV	H,A
	SHLD	CCP$ENT		;SAVE THAT ENTRY ADDRESS
;
	LHLD	BDOS+1		;GET PREVIOUS BDOS ADDRESS
	SHLD	TO$BDOS+1	;SET TO LOCAL REFERENCE VECTOR
;
	LXI	H,L5ENT		;SET NEW BDOS ENTRY TO THIS MODULE
	SHLD	BDOS+1		;BASE+6
;
	CALL	MOVDN		;MOVE DOWN THE BIOS VECTOR TABLE
	LHLD	BWBOOT+1
	SHLD	BIOSLD		;SAVE AS BIOS LOAD ADDRESS
;
	LXI	H,BOOTENT	;SET MOVED DOWN TABLE TO LOCAL BOOT HANDLER
	SHLD	BWBOOT+1
;
	CALL	PRTMSG		;PRINT SIGN ON MESSAGE
	DB	CR,LF,'   MICRO RESOURCES Memory Disk Add-On'
	DB	CR,LF,'               Version 2.0'
 	DB	CR,LF,'           COPYRIGHT (C) 1983'
	DB	CR,LF
	DB	CR,LF,'  Drive M: becomes active in CCS 64K RAM'
	DB	0
;
;
;INITIALIZE ALL ITEMS FOR USE IN THIS I/O HANDLER
;
	MVI	B,ENDZ-STARTZ	;ZERO DATA AREA IN PARAMETER TABLE
	LXI	H,STARTZ
ZLP:
	MVI	M,00H		;PUT IN A ZERO PARM BYTE
	INX	H		;POINT TO NEXT BYTE TO BE ZEROED
	DCR	B		;CHECK BYTE COUNT TO SEE IF DONE
	JNZ	ZLP
;
;
;HAVE THIS UTILITY QUEUE BOTH BIOS AND THIS DRIVER TO THE SAME
;CP/M DATA BUFFER ADDRESS
;
	LXI	D,DEFBUF	;USE DEFAULT BUFFER
	MVI	C,STDMA		;SET DMA FUNCTION CODE
	CALL	TO$BDOS
;
	LXI	H,RAMSEL	;DISABLE DRIVE SELECT FOR RAM DISK
	MVI	M,00H
	XRA	A		;SET RESIDENT DRIVER FLAG OFF
	STA	RESFLG
;
;
;FIND OUT THE SIZE OF THE AVAILABLE MEMORY BOARDS FOR THE 
;MEMORY RESIDENT DRIVE
;
SIZEDRV:
	IF	HARDSIZ
	MVI	B,BDTRKS	;FORCE TRACKS COUNT
	JMP	SIZEGOT
	ENDIF
;
	CALL	PRTMSG
	DB	CR,LF,'Memory sizing ',0
	MVI	A,00H
	STA	SEKSEC		;TEST SECTOR ZERO EACH 'TRACK'
	STA	SEKTRK+1	;ZERO HIGH BYTE OF TRACK NUMBER
;
;
;READ FIRST SECTOR OF EACH "TRACK" TO SAVE EACH FIRST BYTE IN BUFFER
;AND REPLACE THE FIRST BYTE WITH A PATTERN OF TRACK NUMBER
;
	MVI	B,0		;START TRACK NUMBER
GFBYT:
	PUSH	B		;SAVE TRACK NUMBER
	MOV	A,B
	STA	SEKTRK
	CALL	RDSEC		;GET FIRST SECTOR OF TRACK
	POP	B
	LDA	DEFBUF		;GET THE FIRST BYTE
	LXI	H,FBBUF		;POINT AT SAVE BUFFER
	MVI	D,00H
	MOV	E,B
	DAD	D		;INDEX TO BYTE SAVE LOCATION
	MOV	M,A		;SAVE BYTE
	MOV	A,B		;PLACE TRACK NUMBER INTO BYTE
	STA	DEFBUF
	PUSH	B
	CALL	WRTSEC		;WRITE MODIFIED PATTERN BACK OUT
	POP	B
	INR	B		;INCREMENT TRACK COUNTER
	MOV	A,B		;CHECK IF PAST MAX BANK
	CPI	MAXTRK
	JNZ	GFBYT		;MORE BANK "TRACKS" TO DO
;
;
;RESTORE THE FIRST BYTES OF EACH SECTOR TO MAX TRK CNT+1 IN (C)
;BUT CHECK THE FIRST BYTES OF READ BACK IN PATTERN TO SEE EXTENT
;OF AVAILABLE MEMORY
;
BYTRST:
	MVI	B,0		;START TRACK NUMBER
PFBYT:
	PUSH	B		;SAVE TRACK/SEC
	CALL	PRTMSG
	DB	'.',0
	POP	B
	MOV	A,B		;SET TRACK NUMBER
	STA	SEKTRK
	PUSH	B		;SAVE TRK/MAX TRK
	CALL	RDSEC		;GET SECTOR FOR RESTORE
	POP	B
	LDA	DEFBUF		;CHECK BYTE READ BACK
	CMP	B		;DO THEY MATCH?
	JNZ	SIZEGOT
	PUSH	B
	LXI	H,FBBUF		;POINT AT SAVE BUFFER
	MVI	D,00H
	MOV	E,B
	DAD	D		;INDEX TO BYTE SAVE LOCATION
	MOV	A,M		;GET RESTORE BYTE VALUE
	STA	DEFBUF		;RESTORE FIRST BYTE
	CALL	WRTSEC		;WRITE SECTOR BACK
	POP	B
	INR	B		;INCREMENT TRACK COUNTER
	MOV	A,B
	CPI	MAXTRK		;CHECK IF PAST MAX TRACK
	JNZ	PFBYT		;MORE BANK "TRACKS" TO RESTORE
;
;
;HERE IF WE HAVE CURRENT "DISK" SIZE IN 16K TRACKS. TRACKS OR REAL RAM 
;BANKS WERE ASSUMED TO BE PRESENT EACH IN ITS ENTIRITY. ENTRY HERE WITH
;THE (B) REGISTER EQUAL TO THE NUMBER OF THE AVAILABLE TRACKS. THE 
;MAXIMUM REAL TRACK NUMBER IS (B)-1.
;
SIZEGOT:
	MOV	A,B
	CPI	DIRTRK+1	;CHECK IF NO OTHER BANKS AVAILABLE
	JNC	SG1		;CONTINUE IF OK
;
	CALL	PRTMSG
	DB	CR,LF,'No Memory Available for RAM Disk',0
	LHLD	BIOSLD		;TO BIOS WARM BOOT ADDRESS FOR RESTART
	PCHL			;OFF TO BIOS FOR RELOAD
;
SG1:
	STA	NTRKS		;SAVE OFF AS NUMBER OF TRACKS
	SUI	DIRTRK		;SET SIZE OF DISK - RESERVED AREA
	MOV	L,A
	MVI	H,0
	DAD	H		;SHIFT LEFT FOUR TIMES
	DAD	H
	DAD	H
	DAD	H
	PUSH	H		;SAVE SIZE FOR PARM TABLE UPDATE
;
	LXI	D,RSIZE		;POINT TO RAM SIZE STRING AREA
	CALL	BINASC		;CONVERT RAM SIZE TO DEC ASCII
	LXI	H,RSIZE		;FIXUP TO PRINT LEADING ZEROS
	MVI	B,4		;NUMBER OF ZERO STRIP LOCS TO CHECK
SSTRP:
	MOV	A,M		;GET A CHARACTER
	CPI	'0'
	JNZ	STDONE		;NON ZERO SO EXIT
	MVI	M,' '		;SUBSTITUTE A SPACE
	INX	H
	DCR	B
	JNZ	SSTRP		;CONTINUE CHARACTER STRIP ?
;
STDONE:
	CALL	PRTMSG		;PRINT RAM SIZE MESSAGE
	DB	CR,LF,'RAM Available for '
RSIZE:
	DB	'00000 Kbyte Disk',0
;
;
;PLUG DISK SIZE INTO THE DISK PARAMETER TABLE
;
	LXI	H,DPBASE	;INDEX TO DRIVE PARAMETER 
	LXI	D,5		;..TABLE POINTER
	DAD	D
	DAD	D
	MOV	E,M		;GET POINTER
	INX	H
	MOV	D,M
	LXI	H,5		;INDEX TO DISK SIZE
	DAD	D
	POP	D		;POP DISK SIZE INTO THE (DE)
	DCX	D		;-1 FOR BIOS TABLE
	MOV	M,E		;MOVE SIZE INTO TABLE
	INX	H
	MOV	M,D
;
;
;INITIALIZE THE RAM BUFFER TO LOOK LIKE FRESH FORMATTED DRIVE
;THIS FORMAT PROCESS IS OF COURSE OPTIONAL
;
FORMAT:
	CALL	PRTMSG		;PRINT FORMAT MESSAGE
	DB	CR,LF
	DB	CR,LF,'Should RAM be FORMATTED (Y/N): ',0
	CALL	BCIN		;GET RESPONSE
	PUSH	PSW
	MOV	C,A		;ECHO
	CALL	BCOUT
	CALL	PRTMSG
	DB	CR,LF,0
	POP	PSW		;GET INPUT CHAR
	ANI	05FH		;CONVERT TO UPPER CASE
	CPI	'Y'
	JNZ	CCPGO		;SKIP FORMAT IN NOT 'Y'
;
	LXI	H,DEFBUF	;MAKE PATTERN FOR FORMAT
	LXI	B,SECSIZ
E5LP:
	MVI	M,0E5H		;FILL IN AN E5 CHAR FOR FORMAT
	INX	H		;BUMP POINTER
	DCX	B		;DEBUMP BYTE COUNTER
	MOV	A,B		;CHECK IF DONE
	ORA	C
	JNZ	E5LP		;MUST BE MORE TO DO
;
	CALL	PRTMSG
	DB	CR,LF,'Formatting ',0
;
	LDA	NTRKS		;GET TRACK COUNT FOR FORMAT
	MOV	L,A		;(L)=TRACK COUNT
;
	MVI	B,DIRTRK	;START TRACK NUMBER FOR FORMAT
	MVI	C,00H		;START SECTOR NUMBER FOR FORMAT
FMTLP:
	PUSH	H
	PUSH	B
	MOV	A,B
	STA	SEKTRK
	MOV	A,C
	STA	SEKSEC
	CALL	WRTSEC		;WRITE FORMAT SECTOR
	POP	B		;GET BACK TRACK/SECTOR
	POP	H
	INR	C		;BUMP SECTOR NUMBER
	MVI	A,MAXSEC	;AT MAXIMUM SECTOR NUMBER?
	CMP	C
	JNZ	FMTLP		;GO ON TRACK TILL LAST SECTOR
;
	MVI	C,00H		;RESET SECTOR TO TRACK START
	INR	B		;BUMP TRACK NUMBER
;
FMTCK:
	PUSH	H		;SAVE TRACK COUNT
	PUSH	B		;SAVE TRACK/SEC
	CALL	PRTMSG
	DB	'.',0		;PRINT ACTIVITY PERIOD
	POP	B
	POP	H
	DCR	L		;DEBUMP TRACK COUNTER
	JNZ	FMTLP		;GO START NEXT TRACK
;
	CALL	PRTMSG
	DB	CR,LF,0
;
;
;RETURN TO CCP VIA THE OLD DEFINED REENTRY POINT 
;
CCPGO:
	LXI	H,L5ENT		;RESET NEW BDOS ENTRY TO THIS MODULE
	SHLD	BDOS+1		;BASE+6
	LHLD	CCP$ENT		;GET THE CCP ENTRY POINT
	LDA	004H		;GET CURRENTLY LOGGED DRIVE
	MOV	C,A
	PCHL
;
;
;NEW WARM BOOT ENTRY LOCATION THAT RESETS THE DISK SYSTEM
;AND TRANSFERS CONTROL BACK TO THE ALREADY PRESENT CCP
;
BOOTENT:
	JMP	CCPGO		;NOW GO BACK TO THE CCP
;
;
;HERE FROM A BDOS ENTRY TO TRAP FILE OPEN I/O TO CHECK FOR 
;MODULE PRESENT CHECK.
;
BDOS$SCAN:
	PUSH	D		;SAVE CALLERS PARAMETERS
	PUSH	B
	MOV	A,C		;GET FUNCTION CODE TO A
	CPI	OPEN		;SEE IF THIS IS AN OPEN FUNCTION
	JNZ	CHKFAIL
	INX	D		;POINT TO FCB CHECK BYTE
	LXI	H,10		;SET SCAN COUNTER TO FAKE FILE NAME END
	DAD	D
	MVI	B,10		;NUMBER OF "," TO CHECK FOR
SCAN$LOOP:
	MOV	A,M		;GET FILE NAME CHARACTER
	CPI	','
	JNZ	CHKFAIL		;PASS ON IF CHECK FAIL
	DCX	H		;DECREASE BUFFER POINTER
	DCR	B
	JNZ	SCAN$LOOP	;CHECKED ALL POSSIBLE CHARS YET
	MOV	A,M		;CHECK IF ADDRESS BYTE IS OURS
	CPI	MODADDR
	JNZ	CHKFAIL		;BALE OUT IF NOT
	XRA	A		;RETURN ZERO BYTE IF ALL CHECK VALID
	POP	B
	POP	D
	RET			;BACK TO PRESENT CHECKER
;
CHKFAIL:			;PROPER OPEN CHECK FAIL
	POP	B
	POP	D
	JMP	TO$BDOS		;OFF TO THE NORMAL BDOS ROUTINE
;
;
XFRTAB:
;
;	SUBSTITUTE BIOS VECTOR TABLE. THIS JUMP TABLE VECTORS ALL CP/M
;	DISK I/O TO THIS TRANSIENT MODULE FIRST. TABLE IS PUT INTO THE 
;	BIOS VECTOR TABLE POSITION BY A CALL TO THE SUBROUTINE "MOVDN"
;
;
	JMP	BCBOOT		;TO NORMAL BIOS COLD BOOT ROUTINE
	JMP	BOOTENT		;TO LOCAL WARM BOOT HANDLER
	JMP	BCSTAT		;TO NORMAL BIOS CONSOLE STATUS CHECK
	JMP	BCIN		;TO NORMAL BIOS CONSOLE INPUT
	JMP	BCOUT		;TO NORMAL BIOS CONSOLE OUTPUT 
	JMP	BLOUT		;TO NORMAL BIOS LPT OUTPUT
	JMP	BPUN		;TO NORMAL BIOS PUNCH OUTPUT
	JMP	BRDR		;TO NORMAL BIOS READER INPUT
	JMP	HOME		;MOVE DISK TO TRACK ZERO 
	JMP	SELDSK		;SELECT DISK DRIVE 
	JMP	SETTRK		;SEEK TO TRACK IN REG A 
	JMP	SETSEC		;SET SECTOR NUMBER 
	JMP	SETDMA		;SET DISK STARTING ADR 
	JMP	READ		;READ SELECTED SECTOR
	JMP	WRITE		;WRITE SELECTED SECTOR
	JMP	BLSTST		;GO RIGHT TO NORMAL BIOS FOR THIS I/O
	JMP	SECTRAN		;SECTOR TRANSLATE
;
;
LOCTAB:
;
;	LOCAL COPY OF THE ORIGINAL BIOS DISK I/O VECTOR TABLE
;	INITIALIZED BY CALLING THE "MOVDN" SUBROUTINE. 
;
BCBOOT:
	JMP	$-$		;TO BIOS COLD BOOT ROUTINE
BWBOOT:
	JMP	$-$		;TO BIOS WARM BOOT ROUTINE
BCSTAT:
	JMP	$-$		;TO BIOS CONSOLE STATUS CHECK
BCIN:
	JMP	$-$		;TO BIOS CONSOLE INPUT
BCOUT:
	JMP	$-$		;TO BIOS CONSOLE OUTPUT 
BLOUT:
	JMP	$-$		;TO BIOS LPT OUTPUT
BPUN:
	JMP	$-$		;TO BIOS PUNCH OUTPUT
BRDR:
	JMP	$-$		;TO BIOS READER INPUT
BHOME:
	JMP	$-$		;TO BIOS HOME DISK ROUTINE
BSELDSK:
	JMP	$-$		;TO BIOS SELECT DISK ROUTINE
BSETTRK:
	JMP	$-$		;TO BIOS SET TRACK ROUTINE
BSETSEC:
	JMP	$-$		;TO BIOS SET SECTOR ROUTINE
BSETDMA:
	JMP	$-$		;TO BIOS SET DMA ADDRESS ROUTINE
BREAD:
	JMP	$-$		;TO BIOS SECTOR READ ROUTINE
BWRITE:
	JMP	$-$		;TO BIOS SECTOR WRITE ROUTINE
BLSTST:
	JMP	$-$		;TO BIOS LIST STATUS ROUTINE
BSTRAN:
	JMP	$-$		;TO BIOS SECTOR TRANSLATE ROUTINE
;
;
;SUBROUTINE TO INTERCHANGE BIOS DISK I/O VECTOR TABLE ENTRIES WITH
;THOSE CONTAINED LOCALLY.
;
TABSIZ	EQU	17*3		;TABLE SIZE TO INITIALIZE WITH 17 JMP'S
;
;
MOVDN:
	LHLD	BOOT+1		;GET ORIGINAL WARM BOOT VECTOR POINTER
	DCX	H		;ADJUST TO BASE OF COLD BOOT VECTOR
	DCX	H
	DCX	H
	MVI	A,TABSIZ	;SET BYTE COUNT TO MOVE
	STA	BYTCNT
	LXI	D,LOCTAB	;POINT TO LOCAL TABLE FILL FROM ABOVE
	LXI	B,XFRTAB	;POINT TO TABLE TO MOVE UP
MDLP:
	MOV	A,M		;GET A BIOS TABLE BYTE
	STAX	D		;PUT IN LOCAL COPY TABLE
	LDAX	B		;GET BYTE OF PATCH TABLE
	MOV	M,A		;PUT PATCH BYTE INTO BIOS POSITION
	INX	H		;MOVE UP TO NEXT BYTE
	INX	D
	INX	B
	LDA	BYTCNT		;SEE IF DONE YET
	DCR	A
	STA	BYTCNT
	JNZ	MDLP		;CONTINUE IF NOT DONE YET
	RET
;
BYTCNT:
	DB	0		;LOCAL MOVE BYTE COUNTER
;
;
;INLINE PRINT OF MESSAGE TILL A ZERO
;
PRTMSG:
	XTHL			;SAVE HL, GET MSG POINTER
;
PRTMLP:
	MOV	C,M		;GET CHARACTER
	INX	H		;INCREMENT POINTER TO NEXT CHAR
				;..OR RETURN ADDRESS
	MOV	A,C		;CHECK IF ZERO END
	ORA	A
	JZ	PMXIT		;EXIT IF ZERO
;
	PUSH	H		;SAVE MSG PTR.
	CALL	BCOUT		;OUTPUT IT
	POP	H		;RESTORE MSG PTR.
	JMP	PRTMLP		;GO CHECK/DO NEXT CHAR
;
PMXIT:
	XTHL			;RESTORE HL, RET ADDR
	RET			;RET PAST MSG
;
;
;
;ROUTNE TO CONVERT A TWO BYTE BINARY VALUE TO ASCII IN A STRING.
;ASCII IS IN THE DECIMAL NOTATION FAMILIAR TO ALL US HUMANS.
;
;	(HL) = BINARY NUMBER UPON ENTRY
;	(DE) = POINTER TO START OF STRING ON ENTRY
;
BINASC:
	PUSH	PSW		;SAVE REGISTERS
	PUSH	B
	PUSH	D
	LXI	B,004H		;FIX DE TO POINT TO END OF BUFFER
	XCHG
	DAD	B
	XCHG
	PUSH	D		;SAVE ASCII POINTER
	MVI	A,05H		;GET A DIGIT COUNT
	STA	DIGCNT
NDIG:
	LXI	B,-10		;RADIX FOR CONVERSION
	LXI	D,-1		;THIS BECOMES NO DIVIDED BY RADIX
DX:
	DAD	B		;SUBTRACT 10
	INX	D
	JC	DX
	LXI	B,10
	DAD	B		;ADD RADIX BACK IN ONCE
	XCHG
	MOV	A,E
	ADI	'0'		;CONVERT FROM BCD TO ASCII
	POP	D		;GET ASCII BUFFER POINTER
	STAX	D		;STORE ASCII CHARACTER
	DCX	D		;ADJUST ASCII POINTER
	LDA	DIGCNT		;CHECK IF CONVERSION DONE
	DCR	A
	JZ	DDIG		;EXIT IF DONE
	STA	DIGCNT
	PUSH	D		;SAVE ASCII POINTER
	JMP	NDIG		;GO DO NEXT DIGIT
DDIG:
	POP	D		;RESTORE REGISTERS
	POP	B
	POP	PSW
	RET
;
;
;BINASC ROUTINE STORAGE ALLOCATIONS
;
DIGCNT	DB	00		;PLACE TO STORE ASCII DIGIT COUNT
;
;
;********************************************************************
;
;PARAMETER TABLE FOR CCS 2065 BOARDS RESIDENT RAM DRIVE
;
	DISKS	1		;ONE LOGICAL DRIVE SUPPORTED
	DISKDEF	0,1,128,,1024,336,64,0,DIRTRK  ;M: RAM DRIVE
				;SET FOR 64 NON CHECKED DIR ENTRIES
				;336 ALLOCATABLE BLOCKS OF 1K (FOR SEVEN
				;..48K BANK SWITCHED BOARDS) MODIFIED AT 
				;..BOOTUP.
				;OFFSET OF THREE TRACKS TO PERMIT FIRST BANK
				;..TO BE RESERVED FOR CP/M RESIDENT SYSTEM
				;128 SECTORS PER TRACK (128 RECORDS PER 16K
				;..MEMORY BANK).
;
SELDSK:
	MOV	A,C		;GET NEW UNIT NUMBER
	CPI	'M'-041H	;IS THIS OUR DRIVE?
	JZ	SDSK1		;IF SO THEN GIVE THEM A PARAMETER POINTER
;
	XRA	A		;IF NOT CLEAR THE RAM DRIVE SELECT FLAG
	STA	RAMSEL
	JMP	BSELDSK		;IF NOT FOR US THEN LET BIOS HAVE SELECT
;
;
;HERE IF DRIVE SELECT WAS FOR THIS PIECE OF SOFTWARE
;
SDSK1:
	MVI	A,0FFH		;SET THE RAM DRIVE SELECT FLAG
	STA	RAMSEL
;
	LXI	H,DPBASE	;PASS BACK DISK PARAMETER BASE
	XRA	A		;SET A REG. = 00
	RET			;RETURN FROM SELDSK 
;
;
HOME:
	LDA	RAMSEL		;SEE IF RESTORE FOR US
	ORA	A
	JZ	BHOME		;NO MUST BE FOR BIOS DRIVE
;
	LXI	H,00H
	SHLD	SEKTRK		;ZERO SEK TRK ON HOME
	RET
;
;
;SET TRACK NUMBER SPECIFIED BY B&C REGS.
;
SETTRK:
	LDA	RAMSEL		;SEE IF TRACK FOR US
	ORA	A
	JZ	BSETTRK		;TO PROM IF NOT LOCAL
;
	MOV	H,B
	MOV	L,C
	SHLD	SEKTRK		;TRACK TO EMULATE
	RET
;
;
;
;TRANSLATE THE SECTOR GIVEN BY B&C REGS.
;
;	NO TRANSLATE DONE AT THIS TIME. WE WILL NOT NEED TO TRANSLATE 
;	RAM DISK SECTOR BECAUSE RAM HAS NO ROTATIONAL LATENCY
;
SECTRAN:
	LDA	RAMSEL		;SEE IF SECTRAN FOR US
	ORA	A
	JZ	BSTRAN		;TO BIOS IF NOT LOCAL
;
	MOV	H,B
	MOV	L,C
	RET			;RETURN FROM SECTRAN
;
;
;SET DISK SECTOR NUMBER 
;
SETSEC:
	LDA	RAMSEL		;SEE IF SECTOR FOR US
	ORA	A
	JZ	BSETSEC		;TO PROM IF NOT LOCAL
;
	MOV	A,C		;GET SECTOR NUMBER
	STA	SEKSEC		;SECTOR TO EMULATE
	RET			;RETURN FROM SETSEC
;
;
;SET DISK DMA ADDRESS 
;
SETDMA:
	PUSH	H
	MOV	H,B		;MOVE B&C TO H&L
	MOV	L,C
	SHLD	DMAADR		;PUT AT DMA ADR ADDRESS
	POP	H
	JMP	BSETDMA		;TELL BIOS DMA ADDRESS
;
;
;READ THE SELECTED CP/M 2.2 128 BYTE SECTOR
;
READ:
	LDA	RAMSEL		;SEE IF OPERATION FOR US
	ORA	A
	JZ	BREAD		;GO READ IN BIOS IF NOT FOR US LOCAL
;
RDSEC:
	CALL	SWAP		;GO INSTALL DRIVER IF NECESSARY
	CALL	RIOPB		;SET RAM DISK IOPB
	MVI	B,00H		;SET (B) MSB = 0 FOR READ SECTOR
	CALL	IPPROG		;GO READ SECTOR AT RESIDENT LOCATION
;
	LDA	DMAADR+1	;GET USER DMA ADDRESS
				;.. TO CHECK IF IN BANK SWITCH AREA
	CPI	(BANKTOP SHR 8)
	JNC	RDS1		;USED USER BUFFER DIRECTLY FOR SPEED
;
	LXI	D,IPBUF		;BUFFER IN IO PAGE
	LHLD	DMAADR		;POINT AT USER DMAADR AS USUAL
	XCHG			;MOVE FROM IOPAGE TO USER BUFFER
	LXI	B,SECSIZ	;SIZE OF CP/M SECTOR
	LDIR			;BLOCK MOVE THE DATA
;
RDS1:
	XRA	A		;DEFAULT NO READ ERROR
	STA	ERFLAG
	RET
;
;
;WRITE THE SELECTED CP/M 2.2 128 BYTE SECTOR
;
WRITE:
	LDA	RAMSEL		;IS THIS WRITE FOR HERE
	ORA	A
	JZ	BWRITE		;TO BIOS IF SO
;
WRTSEC:
	LDA	DMAADR+1	;GET USER DMA ADDRESS
				;.. TO CHECK IF IN BANK SWITCH AREA
	CPI	(BANKTOP SHR 8)
	JNC	WSEC1		;USE USER BUFFER DIRECTLY FOR SPEED
;
	LXI	D,IPBUF		;BUFFER IN IO PAGE
	LHLD	DMAADR		;POINT AT USER DMAADR AS USUAL
	LXI	B,SECSIZ	;SIZE OF CP/M SECTOR
	LDIR			;BLOCK MOVE THE DATA
;
WSEC1:
	CALL	SWAP		;GO INSTALL DRIVER IF NECESSARY
	CALL	RIOPB		;SETUP RAM DISK IOPB
	MVI	B,080H		;SET (B) MSB FOR WRITE DATA XFR
	CALL	IPPROG		;GO WRITE SECTOR AT RESIDENT LOCATION
	XRA	A
	STA	ERFLAG		;RESET ERROR FLAG
	RET
;
;
;ROUTINE TO INITAILLY INSTALL THE BANK SWITCH MEMORY DRIVER 
;SOFTWARE INTO TE CP/M I/O CONFIGURATION PAGE.
;
SWAP:
	LDA	RESFLG		;IS DRIVER ALREADY RESIDENT
	ORA	A
	RNZ			;RETURN FROM SWAP IF ALREADY PRESENT
;
	IF	NOT BIGMEM	;BLOCK MOVE ONLY REQUIRED IF TO PUT
				;..DRIVER ABOVE BIOS
	LXI	H,RWXFR		;BLOCK MOVE PROGRAM FROM HERE
	LXI	D,IPPROG	;IN I/O CONFIG PAGE
	LXI	B,ENDRWX-RWXFR	;AMOUNT OF PROGRAM CODE TO MOVE
	LDIR			;BLOCK MOVE THE PROGRAM
	ENDIF
;
	MVI	A,0FFH		;MARK DRIVER RESIDENT
	STA	RESFLG
	RET
;
;
;ROUTINE TO SETUP THE RAM DISK SOFTWARE ADDRESS VALUES 
;BASED UPON THE CP/M LOGICAL VALUES. SETS UP THE (A) REGISTER
;TO THE CCS BOARD BANK NUMBER FROM THE CP/M TRACK NUMBER. FIRST
;THREE TRACK NUMBERS ARE ON BANK 0, SECOND THREE ON BANK 1 ETC.
;THE CORRESPONDING BANK NUMBERS ARE LINEAR BIT SELECTS AS 01H FOR 
;BANK 0 AND 02H FOR BANK 1. THE SETUP ALSO INCLUDES THE (HL) PAIR
;SET TO THE PROPERLY INDEXED 128 BYTE SECTOR BASE ADDRESS IN
;THE 48K BANK ENABLED. (DE) IS SET TO THE APPROPIATE DATA BUFFER
;POINTER DEPENDING UPON IF THE USER DMA ADDRESS IS IN THE BANK
;SWITCH AREA OR NOT.
;
RIOPB:
	LDA	SEKTRK		;GET TRACK NUMBER TO COMPUTE
				;BANK NUMBER
	MOV	L,A		;SAVE IN H&L
	MVI	H,00H
	DAD	H		;* 4 FOR FOUR BYTE TABLE ENTRIES
	DAD	H
	LXI	D,PTABL		;INDEX TO DECODING TABLE
	DAD	D		;(HL) IS OFFSET IN TABLE
	MOV	C,M		;GET BANK NUMBER TO (C)
	INX	H
	INX	H
	MOV	E,M		;GET BANK ADDRESS OFFSET TO TRACK
	INX	H
	MOV	D,M
;
	LDA	SEKSEC		;GET THE LOGICAL SECTOR NUMBER
	ORA	A		;CLEAR CARRY
	RAR			;LSB OF SECTOR TO CARRY
	MOV	H,A		;SAVE UPPER SECTOR ADDRESS IN (H)
	RAR			;MOVE LSB TO UPPER BIT OF LOWER
	ANI	080H		;MASK LOWER BITS TO ZERO
	MOV	L,A		;AND PUT INTO (L)
	DAD	D		;(HL) IS THE SECTOR ADDRESS IN BANK
;
	XCHG
	LHLD	DMAADR		;GET USER DMA ADDRESS
	XCHG			;..TO (DE)
	MOV	A,D		;CHECK IF IN BANK SWITCH AREA
	CPI	(BANKTOP SHR 8)
	JNC	RIOPB1		;USE HIS POINTER
	LXI	D,IPBUF		;NO; ..NEED IOPAGE BUFFER
RIOPB1:
	MOV	A,C		;GET BANK TO (A)
	RET
;
;
;TRACK NUMBER TO BANK AND ADDRESS BASE DECODE TABLE
;
BANK	SET	01H		;START BANK NUMBER
PTABL:
	REPT	8		;BUILD FOR EIGHT BOARDS
ADDR	SET	0000H		;TRACK BASE ADDRESS ON BOARD
	REPT	3		;BUILD FOR THREE BANKS PER BOARD
	DW	BANK,ADDR	;TWO WORDS PER ENTRY
ADDR	SET	ADDR+04000H	;BUMP UP BY 16K
	ENDM
BANK	SET	(BANK SHL 1)	;SETUP NEW BANK NUMBER
	ENDM
;
;
;*************************************************************************
;
;	STORAGE AREA FOR VARIABLES BEGINS HERE...
;
;
;RELOCATION POINTER STORAGE AREA
;
CCP$ENT:
	DS	2		;STORE CCP RE-ENTRY POINTER HERE
				;TABLE POINTER HERE
;
BIOSLD:
	DS	2		;PLACE TO SAVE BIOS RELOAD ADDRESS
;
;
;THE NEXT SEVERAL BYTES, BETWEEN STARTZ AND
;ENDZ, ARE SET TO ZERO AT MODULE INITIALIZATION
;
STARTZ	EQU	$		;START OF ZEROED AREA 
;
;
;MEMORY DISK DATA AREA
;
SEKTRK:	
	DS	2		;SEEK TRACK NUMBER
SEKSEC:	
	DS	1		;SEEK SECTOR NUMBER
;
ERFLAG:	
	DS	1		;ERROR REPORTING
;
DMAADR:	
	DS	2		;DISK DMA TRANSFER ADDRESS
;
RAMSEL:
	DS	1		;LOCAL DISK SELECTED FLAG
;
RESFLG:
	DS	1		;SET IF DRIVER CODE IS RESIDENT IN
				;..IN I/O PAGE OF Z80 MEMORY
;
NTRKS:
	DS	1		;STORAGE TO SAVE NUMBER OF TRACKS
;
FBBUF:
	DS	MAXTRK		;FIRST WORD OF TRACKS BUFFER
				;..FOR DISK SIZING CHECK
;
;
;SCRATCH RAM AREA FOR BDOS USE
;
	ENDEF			;LET DISKDEF FIXUP BDOS BUFFERS
;
ENDZ	EQU	$
;
;
;************************************************************************
;
;
;
;POSITION INDEPENDENT ROUTINE THAT MOVES DATA FROM MEMORY DRIVE
;BANKS TO THE EXECUTION PAGE DATA BUFFER. THIS ROUTINE IS DESIGNED TO
;RUN AT THE SOME ARBITRARY BASE PAGE ADDRESS XX00H. THE DATA IS PASSED
;IN 128 BYTE BLOCKS AS THE UPPER HALF OF THE ARBITRARY PAGE IF THE USER 
;DMA ADDRESS WAS IN OR ABOVE THE BANK SWITCH AREA. XX80H BEING THE BASE 
;OF THE BUFFER. IF THE DMA ADDRESS WAS HIGHER THAN THE BANKSWITCH TOP 
;ADDRESS THEN THE TRANSFER BUFFER IS THE CALLERS DMA ADDRESS DIRECTLY
;
;	ENTRY PARAMETERS ARE AS FOLLOWS:
;
;		(A) = BANK NUMBER OF THE CCS 2065
;		      IN FOUR LSB'S
;		(B) = DIRECTION OF TRANSFER
;			MSB = 0 READ EXPANSION MEMORY TO (BC) BUFFER
;			MSB = 1 WRITE (BC) BUFFER TO EXPANSION RAM
;		(DE) = ADDRESS OF TRANSFER DATA BUFFER
;		(HL) = ADDRESS WITHIN BANK SWITCH AREA FROM/TO WHERE
;			DATA BUFFER IS TO BE MOVED.
;
RWXFR:
	OUT	BANKSEL		;SELECT THE APPROPIATE BANK NUMBER
	MOV	A,B		;GET DIRECTION BIT
	ANI	080H		;TEST TRANSFER DIRECTION
	JRZ	XFR		;SKIP DIRECTION SWITCH
	XCHG			;SWAP MOVE POINTERS
XFR:
	LXI	B,SECSIZ	;MOVE ONE SECTOR OF DATA
	LDIR			;MOVE THE DATA WITH BLOCK MOVE
	MVI	A,01H		;RESELECT CCS BANK 0 FOR CP/M
	OUT	BANKSEL
	RET			;BACK TO THE ADD-ON MODULE
;
ENDRWX	EQU	$		;END MARKER FOR READ/WRITE ROUTINE
;
;
;LOCAL DATA TRANSFER BUFFER IF DRIVER IS ABOVE BANK SWITCH TOP
;
	IF	BIGMEM
IPBUF:
	DS	SECSIZ		;DATA BUFFER IF RAM IS BIG ENOUGH
	ENDIF
;
	END
;
;
;+++...END OF FILE
