;---------------- HWREAD.ASM -------------- ; Demonstration for accessing the floppy controller directly ; Reads the bootsector and displays the ID string (at ofs 3) ; Original Read_Sector from a Dutch book, converted to English ; and (re?)commented by Arne de Bruijn, 1994, PD ; Locks up if no disk is inserted in drive 0 (usually A:), until you insert ; a disk ; With HD drives, you need first to switch to the proper density ; with port 3f7h, 0 = 3"5 HD, 1 = 5"25 HD, 2 = DD (not implented, just ; start with a value, if error try another, just like the BIOS does ; (that's why floppies are so slow after you insert a new disk)) ; or just do a dir or something and the BIOS does it all for you ; I've tried to detect a diskchange with a read from port 3f7h, bit 7, ; but after I detected a change, I can't reset the bit (but the BIOS does). ; Anybody? _DATA segment word public 'DATA' buffer db 512 dup(?) ; Buffer for the sector status_buf db 7 dup(?) ; Buffer for the returned status registers _DATA ends _TEXT segment word public 'CODE' assume ds:_DATA,es:nothing,cs:_TEXT,ss:_STACK TestHWR proc far mov ax,_Data mov ds,ax call Read_Sector mov dx,3+offset buffer ; Address of ID string in bootsector mov cx,8 ; 8 Chars mov bx,1 ; To STDOUT mov ah,40h ; Write int 21h mov ax,4c00h ; Terminate w/exitcode 0 int 21h TestHWR endp Read_Sector proc near sti ; Enable interrupts mov dx,3f2h ; Address of Digital Output Register mov al,28 ;00011100 ; bit 1,0: 00 - Drive 0 ; bit 2: FDC enable, bit 3: DMA/IO enable ; bit 4: Motor drive 0 enable out dx,al ; To Digital Output Register call Wait_Motor ; Let moter spin up for 1/2 sec. mov ah,15 ; Command 15: Seek call Send_FDC_Cmd ; Send to FDC mov ah,0 ; Parameter 1: Drive no call Send_FDC_Cmd ; Send to FDC mov ah,0 ; Parameter 2: Track no call Send_FDC_Cmd ; Send to FDC call Wait_Int ; Wait for IRQ 6 from FDC (cmd completed) mov cx,1750 ; Wait to settle head Wait_Head: loop Wait_Head ; A bit CPU dependant, but it works :-) mov al,46h ; Init DMA out 12,al ; ?? out 11,al ; ?? mov ax,offset buffer ; Offset of buffer to AX mov bx,ds ; Segment of buffer to BX mov cl,4 ; Divide by 16 rol bx,cl ; To isolate 64K block (page) of buffer mov dl,bl and dl,0fh ; Page no in DL and bl,0f0h ; Add remainder of segment (*16) add ax,bx ; To offset adc dl,0 ; And adjust DL if neccesary out 4,al ; Store lo byte of offset mov al,ah ; Hi byte to AL out 4,al ; Store hi byte of offset mov al,dl ; Page no to DL out 81h,al ; Store page no of buffer mov ax,511 ; Number of bytes to receive, less 1 out 5,al ; Store lo byte of count mov al,ah ; Hi byte to AL out 5,al ; Store hi byte of count mov al,2 ; ?? out 10,al ; ?? mov ax,351eh ; Get address of INT 1Eh, a pointer to int 21h ; a table with some information for the FDC mov ah,66h ; Command 6: Read data ; bit 5: Skip deleted data adress mark ; bit 6: MFM mode call Send_FDC_Cmd ; Send to FDC mov ah,0 ; Parameter 1: bit 1,0: drive no. (00=drive 0) ; bit 2: Head (1=Head 1) call Send_FDC_Cmd ; Send to FDC mov ah,0 ; Parameter 2: Track no. call Send_FDC_Cmd ; Send to FDC mov ah,0 ; Parameter 3: Head no. (again????) call Send_FDC_Cmd ; Send to FDC mov ah,1 ; Parameter 4: Starting sector no. call Send_FDC_Cmd ; Send to FDC mov ah,es:[bx+3] ; Parameter 5: Sector size (get from table) ; (0 - User spec., 1 - 256, 2 - 512, 3 - 1024) call Send_FDC_Cmd ; Send to FDC mov ah,es:[bx+4] ; Parameter 6: Last sector in track (from table) ; (Actuelly I think this is the last sector to ; read, so for 1 sector starting at sec. 1 ; it's 1, but I don't know for sure) call Send_FDC_Cmd ; Send to FDC mov ah,es:[bx+5] ; Parameter 7: Gap length (from table) call Send_FDC_Cmd ; Send to FDC mov ah,es:[bx+6] ; Parameter 8: Data length (if par5 is 0) call Send_FDC_Cmd ; Send to FDC call Wait_Int ; Wait until completed mov cx,7 ; Command 6 has 7 status registers lea bx,status_buf ; Read them into Status_Buf Next: call Get_FDC_Stat ; Get one status register mov [bx],al ; Store in buffer inc bx ; To next position in buffer loop Next ; Do all the 7 mov dx,3f2h ; Address of Digital Ouput Register mov al,12 ; bit 2: FDC enable, bit 3: DMA/IO enable ; (to turn motor off) out dx,al ; Store to Digital Ouput Register ret ; Return to caller Read_Sector endp Wait_Int proc near ; Wait for command completed interrupt mov ax,40h ; (IRQ 6), by polling a bit in the BIOS data mov es,ax ; area, at 40h:3eh (bit 7) ; This bit is set by the BIOS IRQ 6 (INT 14) ; handler Again: test byte ptr es:[3eh],80h ; Test for the bit jz Again ; Not set? Try again (no timeout check!) and byte ptr es:[3eh],7fh ; And switch off for later use ret ; Return to caller Wait_Int endp Send_FDC_Cmd proc near ; Send a byte (in AH) to 3F5h ; and check for ready mov dx,3f4h ; Address of main status register TryAgain: in al,dx ; Get a byte from main status register test al,80h ; Test bit 7: Data reg ready for IO from/to CPU jz TryAgain ; Not set? Try again (no timeout check!) inc dx ; Set to cmd output address mov al,ah ; Get byte to send out dx,al ; And send to FDC ret ; Return to caller Send_FDC_Cmd endp Get_FDC_Stat proc near mov dx,3f4h ; Address of main status register TryAg2: in al,dx ; Get a byte from main status register test al,80h ; Test bit 7: Data reg ready for IO from/to CPU jz TryAg2 ; Not set? Try again (no timeout check!) inc dx ; Set to cmd status address in al,dx ; And get the status byte ret ; Return to caller Get_FDC_Stat endp Wait_Motor proc near ; Wait a 1/2 sec to let the motor spin up mov ax,40h ; Wait for 9 changes from day-time counter mov es,ax ; In BIOS data area at 40h:6eh mov cx,9 ; 18.2 times per sec incremented, 9 -> 1/2 sec NewTick:mov dx,es:[6ch] ; Get actual tick count (only lo word) ChkTick:cmp es:[6ch],dx ; Compare je ChkTick ; Not changed? Try again loop NewTick ; Wait for next tick ret ; Return to caller Wait_Motor endp _TEXT ends _STACK segment word stack 'STACK' db 256 dup(?) _STACK ends end TestHWR