PAGE ; Read a CP/M 128 byte sector. ; ; EXIT A = 0, successful read operation. ; A = 1, unsucessful read operation. ; Z bit = 1, successful read operation. ; Z bit = 0, unsuccessful read operation. READ: proc CALL CHKBKD ;Check for blocked drive LD AL,#F.RDAT JNC READ1 ! JMP FINAL ;If unBlocked transfer READ1: XOR AL,AL ;Set flag to force a read STO AL,UNACNT ;Clear sector counter CALL FILL ;Fill buffer with data LD ES,DMASEG ;Destination segmentation register LD DI,DMAADR LD DS,BUFSEG ;Set source segmentation register POP SI LD CX,#128/2 ;Move 128 bytes CLD ;Clear direction REP ;Repeat until CX = 0 MOVS ;Move the data to user's memory POP DI ;Restore save registers POP SI POP DS POP ES LD AL,ERFLAG CMP AL,#0 JE READ2 ;If no error LD AL,#0 STO AL,HSTACT ;Clear host active OR AL,#001h ;Set error flag READ2: RET SPACE 4,20 ; Write the selected 128 byte CP/M sector. ; ; ENTRY C = 0, write to a previously allocated block. ; C = 1, write to the directory. ; C = 2, write to the first sector of unallocated ; data block. ; ; EXIT A = 0, write was successful. ; A = 1, write was unsucessful. ; Z bit = 1, write was successful. ; Z bit = 0, write was unsucessful. WRITE: proc .ldr IF @LDR RET .ldr else CALL CHKBKD ;Check for blocked drive JNC WRIT1 ;If blocked transfer LD AL,#F.WRT ;Write to single density floppy JMP FINAL WRIT1: STO CL,WRTYPE CMP CL,#WRUAL JNE WRIT3 ;If write to allocated LD AL,SEKTYP CMP AL,#MAXFTP LD AL,#2048/128 JLE WRIT2 ;If floppy disk LD AL,#4096/128 WRIT2: STO AL,UNACNT LD AX,SEKTRK STO AX,UNATRK ;UNATRK = SEKTRK LD AL,LOGSEC INC AL JMP WRIT4 WRIT3: LD AL,UNACNT CMP AL,#0 JE WRIT5 ;If no unallocated records DEC AL STO AL,UNACNT LD BL,SEKTYP ROR BL LD BH,#0 ADD BX,#LSITT-1 LD AL,UNASEC ;Increment logical sector INC AL CMP AL,[BX] ;Last sector in track table JNE WRIT4 ;If not end of track LD AX,UNATRK INC AX STO AX,UNATRK LD AL,#0 WRIT4: STO AL,UNASEC LD AL,#0FFh WRIT5: CALL FILL LD ES,BUFSEG ;Destination segmentation register POP DI LD SI,DMAADR LD DS,DMASEG ;Source segmentation LD CX,#128/2 ;Move 128 bytes CLD ;Clear direction REP ;Repeat until CX = 0 MOVS ;Move the data to user's memory POP DI ;Restore save registers POP SI POP DS POP ES LD AL,#1 STO AL,HSTWRT ;HSTWRT = 1 LD AL,ERFLAG CMP AL,#0 JNE WRIT7 ;If any errors occurred LD AL,WRTYPE ;write type CMP AL,#WRDIR ;to directory? JNE WRIT6 ;If not directory write CALL FLUSH ;Force write of directory WRIT6: LD AL,ERFLAG CMP AL,#0 WRIT7: RET LSITT: DB 2*26 ;Double 256 byte DB 4*15 ;Double 512 byte DB 8*8 ;Double 1024 byte if gbc20 or gbc10 or gbf40 db 8*11 endif IF disk3 ;All ST506 type drives have 9 sectors/track DB 8*9 ; ... so far. ENDIF .ldr endif PAGE ; TREAD - Determine floppy disk type. ; ; ENTRY C = Selected drive. ; ; Exit Zbit set = no error ; A = disk type (0-3) TREAD: MOV AL,CL ADD AL,#'A' STO AL,NRDYM2 ;Set drive into message CALL SPECIFY ;Set disk parameters LD CX,#240 ;Delay for head selects CALL DELAY LD AL,SEKDSK ;Move drive to command buffer STO AL,ACTDSK ;Set into ACTDSK LD BX,#DSTS LD CX,#DSTSL CALL EXECP ;Perform command LD CX,#1 CALL GCMPS ;Get the one status byte TEST AL,#020h ;Mask ready bit JNZ TRD2 ;If drive is ready LD BX,#NRDYM1 CALL PRINT OR AL,#0FFh ;Clear zero flag TRD1: RET TRD2: AND AL,#008h ;Mask TS bit ROR AL ROR AL ROR AL STO AL,SEKTYP ;Save sided flag LD BX,#RECAL ;Do a test seek LD CX,#LRECAL CALL MOVETO ;Process command JNZ TRD1 ;If error LD AL,#2 ;Seek to track two CALL DOSEEK ;Do seek JNZ TRD1 ;If error LD AL,#F.DRID TRD3: STO AL,DRID LD BX,#DRID LD CX,#DRIDL LD DX,#7 CALL EXECX ;Process command JZ TRD4 ;If read valid LD AL,DRID XOR AL,#040h ;Compliment MFM bit TEST AL,#040h JNZ TRD3 ;If MFM not tried OR AL,#0FFh RET TRD4: LD AL,TEMPBF+6 ;Get number of bytes SHL AL OR AL,SEKTYP ;Combine N with sided flag CMP AL,AL ;Set zero flag RET DSTS: DB F.DSTS,0 DSTSL: EQU *-DSTS RECAL: DB F.RECA,0 LRECAL: EQU *-RECAL DRID: DB F.DRID,0 DRIDL: EQU *-DRID NRDYM1: DB CR,LF,'Drive ' NRDYM2: DB 'x' DB ' not ready.',0 PAGE ; FILL - fill host buffer with approprite host sector. ; ; ENTRY A = 0, Read required if not in buffer. ; 0therwise read not required. ; ; EXIT On exit the stack will contain the following ; values: ; POP x ;x = host record address. ; POP y ;y = caller's buffer address. FILL: STO AL,RDFLAG ;Save read flag LD AL,SEKTYP ;Get disk type CMP AL,#MAXFTP JLE FILL1 ;If floppy disk LD AL,#6 ;2*((LOG BASE 2 (1024)-7) (FOR HARD DISKS) FILL1: ROR AL ;divide by 2 AND AL,#3h MOV CL,AL ;log base 2 (sector size) - 7 LD AL,SEKSEC ;Get sector number LD AH,#0 ROR AX,CL ;Get true sector number STO AL,SEKSEC ADD CL,#7 ;log base 2 (sector size) LD AL,#0 ROL AX,CL ADD AX,#HSTBUF ;Sector in buffer POP CX ;Get return address PUSH ES ;Save destroyed registers PUSH DS PUSH SI PUSH DI PUSH AX PUSH CX ;Set return address LD BX,#HSTACT ;host active flag LD AL,[BX] STOB #1,[BX] ;always becomes 1 CMP AL,#0 JE FILL4 ;If host buffer inactive LD DI,#HSTSEC LD SI,#SEKSEC LD CX,#SEKTYP-SEKSEC+1 MOV AX,CS MOV ES,AX CLD REPE ;REPeat while Equal CMPSB JZ FILL5 ;If all bytes same CALL FLUSH ;Flush host buffer FILL4: LD AX,SEKDSK ;Move disk and type STO AX,HSTDSK STO AX,ACTDSK LD AX,SEKTRK STO AX,HSTTRK STO AX,ACTTRK LD AL,SEKSEC STO AL,HSTSEC STO AL,ACTSEC LD AL,#F.RDAT+040h ;Read double density LD AH,RDFLAG OR AH,AH JZ BLKXFR ;If read required FILL5: RET SPACE 4,10 ; FLUSH - Write out active host buffer onto disk. FLUSH: LD BX,#HSTWRT LD AL,[BX] CMP AL,#0 JE FILL5 ;If host buffer already on disk STOB #0,[BX] LD AX,HSTDSK ;Move disk and type STO AX,ACTDSK LD AX,HSTTRK STO AX,ACTTRK LD AL,HSTSEC STO AL,ACTSEC LD AL,#F.WRT+040h ;Write double density ; JMP BLKXFR SPACE 4,10 BLKXFR: MOV CL,AL LD AX,#HSTBUF STO AX,BUFADR STO DS,BUFSEG MOV AL,CL ; JMP FINAL SPACE 4,10 ; F I N A L -- Preform final transfer processing. ; ; ENTRY AL = Command. ; Act.xxx = transfer parameters. ; NUMSEC= xfer length (if @LDR) ; Exit AL= 0 ==> if successful ; AL= 1 ==> if Error FINAL: CALL PRCDCH ;Process command, drive, cylinder LD BX,#CIOPB ;Set buffer address STO CX,[BX+0] ;Set command and drive STO DX,[BX+2] ;Set cylinder and head LD AL,ACTSEC ;Get sector INC AL STO AX,[BX+4] ;Set sector and N field .ldr if @LDR LD DL,NUMSEC ADD AL,DL DEC AL ;EOT = sector + (N-1) .ldr endif STO AL,[BX+6] ;Set EOT = sector CMP AH,#HMDTYP ;If memory disk JE HMDFNL CMP AH,#4 JL :0 JMP hdfnl ;If hard disk :0: SHL AH MOV AL,AH ;SI= 2 * Type CBW MOV SI,AX LD AX,[SI+CMDTYP] STO AX,[BX+7] ;Set GPL and DTL fields LD AL,#MRTRY ;Set retry count :1: STO AL,RTRY ;Clear retry count LD AL,CIOPB+2 ;Get cylinder number CALL DOSEEK ;Seek to proper track JNZ :3 ;If seek error LD AX,BUFSEG LD CL,#4 ROL AX,CL MOV BX,AX ;save lower 16 bits of paragraph AND AL,#0Fh ;grab upper nibble of paragraph AND BX,#0FFF0h ADD BX,BUFADR ADC AL,#0 ;propagate carry up to upper nibble OUTB DMA ;MSB MOV AL,BH OUTB DMA ;Middle Byte MOV AL,BL OUTB DMA ;LSB LD BX,#CIOPB LD CX,#CIOPL ;Set command buffer length LD DX,#7 CALL EXEC ;perform operation SUB AX,#8040h STO AH,ERFLAG JZ :4 ;If no errors :3: LD AL,RTRY ;Get retry counter DEC AL JNZ :1 ;If not permanent error OR AL,#01h STO AL,ERFLAG ;Set error flag :4: RET ;********************************************************** ;* * ;* M-DRIVE/H Final processing * ;* CIOPB + 0 - Command * ;* + 2 - Track * ;* + 4 - Sector * ;* * ;********************************************************** HMDFNL: IF HDRIVE LD AL,#MRTRY STO AL,[RTRY] ;Initialize retry count HMFNL1: LD AX,[CIOPB+2] ;Get track LD CX,#128 ; MUL CX ;Track * 128 is EA MOV BX,AX ;Into BX for set routine LD AL,[CIOPB+4] ;Get Sector # DEC AL ;Actual is 1 less CALL SET CLD ;Clear direction flag for increment LD DX,#HDATA LD CX,#128 ;128 bytes to move MOV AH,CL ;For parity LD AL,CIOPB ;Get command CMP AL,#F.RDAT ;Is command read? JNE HMWRT ;If write jump, else do read PUSH ES ;Save regs for string commands LES DI,[BUFADR] ;Get buffer offset ; ; and get buffer segment HMREAD: INB DX ;Get a byte from MDRIVE STOSB ;Store it in buffer ADD AH,AL ;Add parity LOOP HMREAD ;Loop until CX is 0 (128) POP ES CALL PARITY ; INB HDATA ;Get Parity SUB AL,AH ;Check for error JZ HMDONE ;IF no error DEC [RTRY] JNZ HMFNL1 ;Go retry .hdrive ENDIF LD AL,#1 STO AL,[ERFLAG] HMDONE: RET .hdrive IF HDRIVE ; HMWRT: PUSH DS ;Save segment register LDS SI,[BUFADR] ;Get buffer offset ; get buffer segment HMWLP: LODSB ;Get a byte from preset buffer OUTB DX,AL ;To M-DRIVE/H ADD AH,AL ;Add parity LOOP HMWLP ;Loop till CX = 0 (128) POP DS ;Restore segment registers CALL PARITY ;Set board for parity MOV AL,AH ;Get parity OUTB HDATA ; XOR AX,AX ;Show no error RET ; ; PARITY: LD AL,[CIOPB+4] ;Get sector DEC AL ;Bias LD BX,[CIOPB+2] ;Get track ; SET: ;Set MDRIVE board with 3 byte AL=highest,BX=2 lowest OUTB HADDR MOV AL,BH OUTB HADDR MOV AL,BL OUTB HADDR RET ; .hdrive ENDIF hdfnl: IF HARD .D2 IF DISK2 call hdsel ;Select drive sto al,erflag jnz hdfnl0 ;If select error ld al,#mrtry hdfnl1: sto al,rtry call hdseek ;Seek to correct track call hdxfer ;Perform hard disk transfer sto al,erflag jz hdfnl0 ;If no errors dec rtry jnz hdfnl1 ;If attempts left ld bl,actdsk ld bh,#0 stob #-1,[bx+hdcyl] LD AL,#0 OR AL,#1 STO AL,ERFLAG hdfnl0 RET .D2 ENDIF ; ;****************************************************** ; Disk 3 driver Floppy CIOPB contains drive,sector etc. ;****************************************************** ; .D3 IF DISK3 LD BX,#D3IOPB ;Get start of IOPB LD SI,#CIOPB LD AL,[SI] ;Get R/W command STOB AL,[BX+RWF] ;Put command in iopb LD AL,[SI+1] ;Get drive # STOB AL,[BX+DRV] ;Set in IOPB LD AX,[SI+2] ;Get current track STO AX,[BX+TRK] ;Set track in IOPB LD AL,[SI+4] ;Only 1 byte used for sector in CP/M DEC AL ;Sectors are 0-8 XOR AH,AH ;Clear high byte of sector number STO AX,[BX+SEC] ;Set sector in IOPB LD AX,[BUFSEG] ;Get offset of DMA buffer LD CL,#4 ROL AX,CL MOV DX,AX AND AL,#0FH AND DX,#0FFF0H ADD DX,[BUFADR] ADC AL,#0 STO DX,[BX+D3DMA] ;And put into IOPB STOB AL,[BX+D3DMA+2] ;And to high byte STOB #D3TSRW,[BX+CMND] ;Want track read/write command STO #1,[BX+NUMSECS] ;Only read 1 sector STOB #0,[BX+STAT] ;Zero out status byte LD AL,#ATTN OUTB D3PORT,AL D3WAIT: CMPB [BX+STAT],#0 ;Wait for status byte to be nonzero JE D3WAIT CMPB [BX+STAT],#0FFH ;If status is 0ffh => no error JNE D3ERROR ;Otherwise error LD AL,#0 RET ; D3ERROR: LD BX,#D3IOPB STOB #D3REL,[BX] ;Set relocate command in iopb STOB #BUSY,[BX+STAT] ;Clear status byte LD AL,#ATTN OUTB D3PORT,AL REL1: CMPB [BX+STAT],#BUSY JE REL1 LD AL,#00 ;Show no error CMPB [BX+STAT],#COMPL ;See if relocate succesful JNE D3ERR1 ;If not, very hard error CMPB [BX+RWF],#0 ;If command was read return error JNE D3ERR2 ; else return ok D3ERR3: OR AL,#01 ;Show error on read STO AL,ERFLAG D3ERR2: RET D3ERR1: LD BX,#D3FATL ;Get bad disk3 error message LD AL,[D3IOPB+STAT] ;Get error number ADD AL,#'0' ;Add in ASCII bias STOB AL,[D3FTL1] ;Put in message CALL PRINT JMP D3ERR3 D3FATL: DB CR,LF,'Fatal error from DISK3, error number ' D3FTL1: DB '0.',CR,LF,0 .D3 ENDIF endif RTRY: DB 0 MRTRY: EQU 10 ;Maximum retry count ; Command buffer disk type dependent values. CMDTYP: ; GPL DTL DB 007H,128 ;Single density DB 00EH,255 ;Double density 256 bytes DB 01BH,255 ;Double density 512 bytes DB 035H,255 ;Double density 1024 bytes SPACE 4,10 ; PRCDCH -- Process Command, Drive, Cylinder, and Head. ; ; ENTRY A = command. ; ; EXIT AH = N field (0..4). ; CH = drive. ; CL = command. ; DH = head. ; DL = cylinder. PRCDCH: MOV CL,AL ;Save Command LD CH,ACTDSK LD DX,ACTTRK ;Get track number LD AH,ACTTYP ;Get type CMP AH,#HMDTYP ;If Memory disk JZ CDCHMD ;Done CMP AH,#MAXFTP JG CDCH2 ;If hard disk TEST AH,#1 JZ CDCH1 ;If single sided MOV AL,DL AND AL,#1 MOV DH,AL ;Set head ROL AL ROL AL OR CH,AL ;Combine head with drive SHR DL ;Adjust track for cylinder CDCH1: SHR AH CDCHMD: RET CDCH2: .h IF HARD LD AH,#4 ;Force type = 4 .D2 IF DISK2 MOV AL,DL if gbc20 and al,#7 shr dx endif if gbc10 and al,#3 endif if gbc26 and al,#7 shr dx endif SHR DX ;Remove head from track SHR DX MOV DH,AL AND CL,#00Fh CMP CL,#F.RDAT LD CL,#H.red JZ CDCH3 ;If read command LD CL,#H.wrt .D2 ENDIF ; .D3 IF DISK3 AND CL,#0FH ;Isolate command CMP CL,#F.RDAT LD CL,#D3RD ;DISK3 wants 1 for a read JE CDCH3 LD CL,#D3WRT ;and a 0 for a write .D3 ENDIF ; CDCH3: .h endif RET SPACE 4,10 ; Seek to specified Track/Sector ; ; Entry A = Track DOSEEK: STO AL,DSEKC+2 LD BX,#DSEKC LD CX,#DSEKL ; JMP MOVETO ; Move head according to command. ; ; ENTRY BX = address of command buffer. ; CX = length of command buffer. ; ; Exit Z bit set if no error. MOVETO: CALL EXECP ;Perform seek MVTO1: INB INTS CMP AL,#0 JNS MVTO1 ;if not complete LD AL,#F.RSTS OUTB FDCD ;request status LD CX,#2 CALL GCMPS ;Get status CMP AL,#20h JZ MVTO2 ;If seek complete LD CL,TEMPBF ;Get true status byte AND CL,#3h ;Mask disk unit LD AL,ACTDSK CMP AL,CL JNE MVTO1 ;If not proper unit OR AL,#001h ;Clear zero flag MVTO2: RET SPACE 4,10 ; SPECIFY - Specify disk drive characteristics. SPECIFY: LD BX,#SPEC LD CX,#LSPEC LD DX,#0 LD AL,STEPMS OR AL,#HUT STO AL,[BX+1] JMP EXEC ;Specify disk command DSEKC DB F.SEEK,0,0 DSEKL: EQU *-DSEKC SPEC DB F.SPEC VFD 4\SRT,4\HUT VFD 7\HLT,1\ND LSPEC EQU *-SPEC PAGE ; E X E C ; Entry BX = FWA of command buffer. ; CX = # of bytes to output ; DX = # of bytes for status ; ; Exit If C <> 0 then see GCMPS. EXECP: LD DX,#0 ;Set no status byte EXECX: LD AL,ACTDSK ;Set drive into command buffer STO AL,[BX+1] EXEC: EXEC1: INB FDCS CMP AL,#0 JNS EXEC1 ;if no master ready bit LD AL,[BX] ;command byte OUTB FDCD ;to controller INC BX LOOP EXEC1 ;If more bytes OR CX,DX JZ :30 ;if no status bytes EXEC2: INB INTS CMP AL,#0 JNS EXEC2 ;If operation not complete SPACE 4,10 ; Get completion status. ; ; Entry CX = # of status bytes to read ; ; Exit TEMPBF = status bytes read in. ; A = [TEMPBF] and 0F8h. ; Flags set according to above value in A. GCMPS: LD BX,#TEMPBF ;Set status buffer address GCMPS2: INB FDCS CMP AL,#0 JNS GCMPS2 ;if not ready INB FDCD ;Get status byte STO AL,[BX] INC BX LOOP GCMPS2 ;If more bytes LD AX,TEMPBF ;Get two status bytes AND AL,#0F8h :30 RET