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 hard if gbc20 db 8*11 endif if gbc10 db 8*11 endif if gbc26 db 8*18 endif 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 ;Save LD AL,CIOPB+4 ;Get Sector # DEC AL ;Actual is 1 less CALL SET PUSH DI ! PUSH SI PUSH ES ! PUSH DS ;Save regs for string commands LD SI,BUFADR ; LD BX,BUFSEG ;Get buffer segment LD AL,CIOPB ;Get command MOV DS,BX ; LD CX,#128 ;128 bytes to I/O MOV DL,CL ;For parity CLD ;Clear direction flag CMP AL,#F.RDAT ;Is command read? JNE HDINTO ;If write jump ; JMP HDOTOF MOV DI,SI ;Change directions MOV AX,DS MOV ES,AX ; HDOTOF: INB HDATA ;Get a byte from MDRIVE STOSB ;Store it in buffer ADD DL,AL ;Add parity LOOP HDOTOF ;Loop until CX is 0 (128) POP DS ! POP ES ;RESTORE INDEX REGISTERS POP SI ! POP DI CALL PARITY ; INB HDATA ;Get Parity SUB AL,DL ;Check for error JZ HDONE ;IF no error LD AL,RTRY ;Get retry count DEC AL ;Count error STO AL,RTRY ; JNZ HMFNL1 ;Go retry .hdrive ENDIF LD AL,#0 OR AL,#1 STO AL,ERFLAG HDONE: RET .hdrive IF HDRIVE ; HDINTO: LODSB ;Get a byte from preset buffer OUTB HDATA ;To MDRIVE ADD DL,AL ;Add parity LOOP HDINTO ;Loop till CX = 0 (128) POP DS ! POP ES ;RESTORE INDEX REGISTERS POP SI ! POP DI CALL PARITY ;Set board for parity MOV AL,DL ;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 ; CALL SET ; ; RET 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 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] endif LD AL,#0 OR AL,#1 STO AL,ERFLAG hdfnl0 RET 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 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 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