page 0 title 'XDRIVE Version 1.0 Aug. 24, 1983' ***************************************************************** * * * XDRIVE is a program that permits extended memory to function * * as a fast disk drive. This program does not require any * * modification of the existing BIOS. When run, this program * * will move into high memory above the existing BIOS, and patch * * into the BIOS's jump table. This program requires a Z80 CPU * * to run, and should work with any extended memory size from * * 2Kb to 8Mb. Also this program will work in an interrupt * * environment (many simular public domain programs have fatal * * errors in systems that use interrupts or where extended ram * * is greater than 256Kb). MAC and Z80.LIB are required for * * assembly. * * * * Copyright 1983 by; * * * * Paul R. Grupp * * Via ...GRUPP@MIT-MC * * Via CompuServe 71615,1034 * * * * and * * * * Herbert B. Shore * * Department of Physics * * San Diego State University * * San Diego, CA 92182 * * (619) 265-6159 * * * * Permission is granted for non-commercial use only. * * * ***************************************************************** ; ; ; THE FOLLOWING EQUATES ARE SYSTEM DEPENDENT. ; ; XDRIVE: Set to memory location above BIOS. A little ; less than 1K of GLOBAL memory is required at this ; location; i.e. the same block of memory must be ; accessible independent of the setting of the extended ; address bus. ; ; PRAM: The extended address of regular program RAM. ; Usually this is 00H. ; ; DRAM: The extended address of the first extra segment ; of RAM to be used as a simulated disk drive. Each ; segment can be up to 63K long (the upper 1K is not ; accessible, since it is occupied by the global RAM). ; The extended addresses of the extra segments should be ; sequential; the program will automatically determine ; how many extra segments there are. ; ; XPORT: The output port that sets the extended address ; bus. ; ; AVL: The number of available kilobytes in each extra ; segment of RAM. This memory must all be NON-global. ; If, for example, the upper 16K of your memory is global, ; set AVL to 48K. ; ; DNAME: The alphabetic designation of the RAM drive. ; ; PANEL: If you have an (IMSAI-type) front panel, set ; PANEL=TRUE. The lights will display the extended ; address, simulating the LED on the door of the disk ; drive. Flipping switch D0 up will "write-protect" ; the RAM drive. ; ; LIGHTS: The output port for front panel lights. ; ; SWITCH: The input port for front panel switches. ; ; DIR$SM: The number of directory entries permitted on the drive ; if the total RAM is 256K or less. This will be rounded to ; the next higher multiple of 32. 32 will leave the maximum ; space for program files (recommended). ; ; DIR$MD: The number of directory entries permitted on the drive ; if the total RAM is 257K to 512K. This will be rounded ; to the next higher multiple of 64. (64 recommended) ; ; DIR$LG: The number of directory entries permitted on the drive ; if the total RAM is more than 512K. This will be rounded ; to the next higher multiple of 64. (128 or more recommended) ; ; FALSE EQU 0 TRUE EQU NOT FALSE ; XDRIVE EQU 0FC00H ;RUNNING MEMORY LOCATION PRAM EQU 00H ;EXTENDED ADDRESS OF MAIN RAM DRAM EQU 01H ;FIRST EXTENDED ADDRESS FOR XDRIVE XPORT EQU 0FDH ;PORT TO SET EXTENDED ADDRRESS AVL EQU 63 ;AVAILABLE KBYTES/BOARD DNAME EQU 'M' ;DRIVE NAME OF XDRIVE PANEL EQU FALSE ;IMSAI FRONT PANEL IF PANEL LIGHTS EQU 0FFH ;FRONT PANEL LIGHTS SWITCH EQU 0FFH ;FRONT PANEL SWITCHES ENDIF DIR$SM EQU 32 ;# OF DIR ENTRIES FOR MEM SIZE UP TO 256K DIR$MD EQU 64 ;# OF DIR ENTRIES FOR MEM SIZE 257K TO 512K DIR$LG EQU 128 ;# OF DIR ENTRIES FOR MEM SIZE > 512K ; ; THESE EQUATES WILL NORMALLY NOT BE CHANGED. ; TPA EQU 100H ;START OF TPA CR EQU 0DH LF EQU 0AH BDOS EQU 5 DELCHR EQU 0E5H ;DELETED DIRECTORY ENTRY DNUMB EQU DNAME-'A' ;XDRIVE DRIVE NUMBER DIRS EQU ((DIR$SM+31)/32)*32 ;FORCE TO EVEN 1K BLOCK SIZE DIRM EQU ((DIR$MD+63)/64)*64 ;FORCE TO EVEN 2K BLOCK SIZE DIRL EQU ((DIR$LG+63)/64)*64 ;FORCE TO EVEN 2K BLOCK SIZE ; MACLIB Z80 $*MACRO ; ; ORG TPA ; POP H ;GET CCP RETURN ADDRESS FROM STACK SHLD RETCCP+1 ;SAVE IT LXI SP,BOTTOM+100 ;SET UP OUR OWN STACK LHLD 1 ;HAS BIOS BEEN MODIFIED YET? INX H ;ADDRESS IN BIOS JUMP VECTOR MOV E,M INX H MOV D,M XCHG ;WBOOT ADDRESS --> HL LXI D,XDRIVE ANA A ;CLEAR CARRY DSBC DE ;WBOOT - XDRIVE JC CONTIN ;IF CARRY, THEN WE ARE OK LXI D,RERUN ;PRINT ERROR MESSAGE MEXIT: MVI C,9 CALL BDOS EXIT: LXI D,CRLF MVI C,9 CALL BDOS RETCCP: JMP $ ;RETURN TO CCP (JMP FILLED IN BY PGM) ; CONTIN: LXI D,SIGNON ;PRINT SIGNON MESSAGE MVI C,9 CALL BDOS LXI H,START ;SOURCE ADDRESS LXI D,XDRIVE ;DESTINATION LXI B,BOTTOM-START ;COUNT TO MOVE LDIR LHLD 1 ;GET ADDRESS OF WARM BOOT LXI D,WBOOT1 ;TRANSFER BIOS JUMP TABLE TO OURS LXI B,48 ;48 BYTES LDIR LXI H,WBOOT2 ;TRANSFER OUR TABLE TO BIOS LDED 1 LXI B,48 LDIR MVI C,25 ;BDOS FUNCTION RETURN CURRENT DISK CALL BDOS ;DRIVE IN A MOV C,A ;SET UP TO CALL BIOS SELDSK CALL WBOOT1+24 ;SELDSK RET HL = DPH ADDRESS LXI D,8 ;OFFSET TO DIRBUF IN DPH DAD D MOV E,M ;GET LO ADDRESS INX H MOV D,M ;GET HI ADDRESS SDED DIRBUF ;COPY TO OUR DPH LDAI ;GET INTERRUPT STATUS PUSH PSW ;SAVE IT DI ;NO INTERRUPTS ALLOWED IN EXTENDED RAM CALL SETUP ;GET AVAILABLE RAM SIZE POP PSW ;GET BACK INTERRUPT STATUS JPO $+4 ;INT WAS DI, SO DON'T EI EI ;TURN INTERRUPTS BACK ON DCR B JNZ RAMOK ;AT LEAST ONE BANK? LXI D,NORAM ;NO RAM AT ALL JMP MEXIT ;PRINT MSG AND EXIT TO CCP ; RAMOK: LXI H,0 ;CLEAR FOR MULT LXI D,AVL ;KBYTES PER BANK MULT: DAD D DCR B JNZ MULT ;AVL*BANKS DCX H ;1K BLOCKS -1 FOR DSM SHLD DSM XRA A ORA H ;DSM < 256? INX H JZ DPB1K ;YES, DPB IS OK THEN LXI D,(15*100H)+4 ;D=15, E=4 SDED BSH ;BSH=4, BLM=15 SRLR H ;INT DIVIDE BY 2 TO GET NUMBER OF 2K BLOCKS RARR L ;...16 BIT DIVIDE DCX H ;2K BLOCKS -1 FOR DSM SHLD DSM LXI D,DIRM-1 SDED DRM MVI A,LOW(0FFH SHL(8-DIRM/64)) STA AL0 MVI A,1 STA EXM XRA A ORA H ;DSM < 256? JZ DPB2K ;YES, DPB IS OK THEN LXI D,DIRL-1 SDED DRM MVI A,LOW(0FFH SHL(8-DIRL/64)) STA AL0 XRA A STA EXM DPB2K: INX H ;GET BACK NUMBER OF 2K BLOCKS DAD H ;TIMES 2 = 1K BLOCKS DPB1K: CALL DECOUT ;DECIMAL PRINT DISK SPACE IN KBYTES LXI D,MEMMSG MVI C,9 CALL BDOS LXI D,QUEST ;ASK IF TO ERASE XDRIVE DIRECTORY MVI C,9 CALL BDOS MVI C,1 ;GET CHARACTER CALL BDOS RES 5,A ;CONVERT TO UPPER CASE CPI 'Y' JNZ EXIT JMP CLEAR ; ; NEW TABLE COPIED INTO BIOS ; WBOOT2: JMP WBOOT JMP WBOOT1+3 JMP WBOOT1+6 JMP WBOOT1+9 JMP WBOOT1+12 JMP WBOOT1+15 JMP WBOOT1+18 JMP HOME ;21 JMP SELDSK ;24 JMP SETTRK ;27 JMP SETSEC ;30 JMP SETDMA ;33 JMP READ ;36 JMP WRITE ;39 JMP WBOOT1+42 JMP SECTRN ;45 ; ; SIGNON: DB CR,LF, 'Linking ', DNAME, ': Drive...', CR,LF,'$' RERUN: DB CR,LF, DNAME, 'DRIVE already active.$' NORAM: DB 'Can''t activate ', DNAME, 'DRIVE. No available RAM.$' MEMMSG: DB 'k available on ', DNAME, ':', CR,LF,'$' QUEST: DB 'ERAse ', DNAME, ':*.* (Y/N)? $' CRLF: DB CR,LF,'$' ; ; DECOUT: PUSH PSW ;PRINT HL IN DECIMAL PUSH B PUSH D PUSH H LXI B,-10 LXI D,-1 DECOU1: DAD B INX D JC DECOU1 LXI B,10 DAD B XCHG MOV A,H ORA L CNZ DECOUT MOV A,E ADI '0' MVI C,6 MOV E,A CALL BDOS ;PRINT DIGIT ON CONSOLE POP H POP D POP B POP PSW RET ; START: OFFSET EQU XDRIVE-$ ***************************************************************** * * * Relocated code starts here. All labels from here on MUST * * be in the form of LABEL: EQU $+OFFSET * * * ***************************************************************** ; ; ORIGINAL JUMP TABLE FROM BIOS ; WBOOT1: EQU $+OFFSET DS 48 ; ; IMPLEMENT BIOS FUNCTIONS ; WBOOT: EQU $+OFFSET LXI H,80H ;SET INITIAL DMA ADDR SHLD DMAADR JMP WBOOT1 ;BACK TO BIOS ; ; HOME: EQU $+OFFSET LDA DISK ;CHECK DISK NUMBER CPI DNUMB ;IS IT THE XDRIVE? JNZ WBOOT1+21 ;IF NOT, LET BIOS HANDLE IT MVI A,DRAM ;SET "TRACK" TO 0 STA XTRAK RET ; ; SELDSK: EQU $+OFFSET MOV A,C ;CHECK REQUESTED DISK, STA DISK CPI DNUMB JNZ WBOOT1+24 LXI H,DPH ;RETURN DISK PARAMETER HEADER RET ; ; SETTRK: EQU $+OFFSET LDA DISK CPI DNUMB JNZ WBOOT1+27 MOV A,C ;TRACK NUMBER ADI DRAM ;CONVERT TO EXT ADDRESS STA XTRAK RET ; ; SETSEC: EQU $+OFFSET LDA DISK CPI DNUMB JNZ WBOOT1+30 MVI L,0 ;SELECT NUMBER IN BC MOV H,C ;MULTIPLY BY 128 RARR B ;RESULT IS IN HL RARR H RARR L SHLD SECTOR ;SAVE IT RET ; ; SETDMA: EQU $+OFFSET SBCD DMAADR ;SAVE DMA ADDRESS BOTH HERE JMP WBOOT1+33 ;AND IN BIOS ; ; READ: EQU $+OFFSET LDA DISK CPI DNUMB JNZ WBOOT1+36 LDAI ;GET INTERRUPT STATUS PUSH PSW ;SAVE IT DI ;NO INTERRUPTS ALLOWED IN EXTENDED RAM LDA XTRAK ;SET EXTENDED ADDRESS OUT XPORT IF PANEL CMA OUT LIGHTS ENDIF LHLD SECTOR ;DO BLOCK MOVE TO LXI D,RAMBUF ;TEMPORARY BUFFER LXI B,128 ;SECTOR=128 BYTES LDIR MVI A,PRAM ;RESET ADDRESS OUT XPORT IF PANEL CMA OUT LIGHTS ENDIF POP PSW ;GET BACK INTERRUPT STATUS JPO $+OFFSET+4 ;INT WAS DI, SO DON'T EI EI ;TURN INTERRUPTS BACK ON LXI H,RAMBUF ;MOVE FROM BUFFER LDED DMAADR ;BACK TO MAIN RAM LXI B,128 LDIR XRA A ;NO ERRORS RET ; ; WRITE: EQU $+OFFSET LDA DISK CPI DNUMB JNZ WBOOT1+39 IF PANEL IN SWITCH ;CHECK D0 OF FRONT PANEL ANI 1 RNZ ;RETURN IF WRITE PROTECT ENDIF LHLD DMAADR ;DMA --> BUFFER LXI D,RAMBUF LXI B,128 LDIR LDAI ;GET INTERRUPT STATUS PUSH PSW ;SAVE IT DI ;NO INTERRUPTS ALLOWED IN EXTENDED RAM LDA XTRAK OUT XPORT IF PANEL CMA OUT LIGHTS ENDIF LXI H,RAMBUF ;BUFFER --> XDRIVE LDED SECTOR LXI B,128 LDIR MVI A,PRAM OUT XPORT IF PANEL CMA OUT LIGHTS ENDIF POP PSW ;GET BACK INTERRUPT STATUS JPO $+OFFSET+4 ;INT WAS DI, SO DON'T EI EI ;TURN INTERRUPTS BACK ON XRA A RET ; ; SECTRN: EQU $+OFFSET LDA DISK CPI DNUMB JNZ WBOOT1+45 MOV H,B MOV L,C RET ; ; DISK PARAMETER HEADER FOR XDRIVE ; DPH: EQU $+OFFSET DW 0 ;NO SECTOR TRANSLATION DW 0,0,0 ;SCRATCH DIRBUF: EQU $+OFFSET DW 0 ;DIRECTORY BUFFER POINTER (FILLED IN BY SETUP) DW DPB ;DISK PARAMETER BLOCK DW 0 ;NO DIRECTORY CHECK AREA NEEDED DW ALV ;ALLOCATON VECTOR ; ; DISK PARAMETER BLOCK FOR XDRIVE ; DPB: EQU $+OFFSET DW AVL*(1024/128) ;SECTORS PER TRACK BSH EQU $+OFFSET DB 3 ;BSH (SETUP CHANGES AS NEEDED) DB 7 ;BLM (SETUP CHANGES AS NEEDED) EXM: EQU $+OFFSET DB 0 ;EXM (SETUP CHANGES AS NEEDED) DSM: EQU $+OFFSET DW 0 ;MAX BLOCK NUNBER (FILLED IN BY SETUP) DRM: EQU $+OFFSET DW DIRS-1 ;MAX DIRECTORY SIZE (SETUP CHANGES AS NEEDED) AL0: EQU $+OFFSET DB LOW(0FFH SHL(8-DIRS/32)) ;DIR BLOCKS (SETUP CHANGES AS NEEDED) DB 0 ;AL1 DW 0 ;NO CHECKED DIRECTORY ENTRIES DW 0 ;NO SKIPPED TRACKS ; ; DATA STORAGE AREA ; DISK: EQU $+OFFSET DB 0 XTRAK: EQU $+OFFSET DB DRAM DMAADR: EQU $+OFFSET DW 80H SECTOR: EQU $+OFFSET DW 0 RAMBUF: EQU $+OFFSET DS 128 ALV: EQU $+OFFSET ; ***************************************************************** * * * The following code is executed once to set up the XDRIVE. * * In order to conserve space it will be overwritten by data * * buffers during actual access to the XDRIVE. * * * ***************************************************************** ; ORG RAMBUF-OFFSET ;BACK UP TO BUFFER SPACE ; SETUP: EQU $+OFFSET ; ; DETERMINE NUMBER OF MEMORY BANKS ; MVI B,0 SIZEUP: EQU $+OFFSET MOV A,B CPI (8192/AVL)+1 ;HAVE > 8 MBYTES? RZ ;ONLY ALLOW 8 MBYTE FOR DRIVE ADI DRAM ;GET EXTENDED ADDRESS OUT XPORT INR B ;FOR NEXT LOOP LXI H,TPA ;SAMPLE ADDRESS WHERE RAM STARTS IN SYSTEM MOV A,M ;GET A SAMPLE MOV C,A ;SAVE IT CMA ;COMPLEMENT SAMPLE MOV M,A ;TRY TO PUT INTO MEM MOV A,M ;SEE IF IT STUCK MOV M,C ;RESTORE ORIGINAL XRA C ;SHOULD GIVE 0FFH INR A ;SHOULD GIVE 0 MVI A,PRAM OUT XPORT ;RESTORE CP/M BANK JZ SIZEUP ;LOOP TO NEXT BANK RET ; ; ; CLEAR DIRECTORY OF XDRIVE ; CLEAR: EQU $+OFFSET LDAI ;GET INTERRUPT STATUS PUSH PSW ;SAVE IT DI ;NO INTERRUPTS ALLOWED IN EXTENDED RAM MVI A,DRAM ;EXT ADDRESS OF XDRIVE OUT XPORT IF PANEL CMA OUT LIGHTS ;TO IMSAI FRONT PANEL ENDIF LXI H,0 ;LOCATION 0000 LXI D,32 ;EVERY 32 BYTES LBCD DRM ;NUMBER OF ENTRIES -1 INX B ;+1 CLOOP: EQU $+OFFSET MVI M,DELCHR DAD D DCX B MOV A,B ORA C ;BC = 0? JNZ CLOOP MVI A,PRAM ;RESET EXT ADDRESS OUT XPORT IF PANEL CMA OUT LIGHTS ENDIF POP PSW ;GET BACK INTERRUPT STATUS JPO $+OFFSET+4 ;INT WAS DI, SO DON'T EI EI ;TURN INTERRUPTS BACK ON JMP EXIT ; ; BOTTOM EQU $ ;USED FOR MOVER (NOT $+OFFSET) END