Andrew Dent
4165acdf83
- To better support git, remove SVN dependant `$Revision$` from file headers. This does *not* remove: the use of `__REV__` macro in `boostr.inc` and `kernel.asm` - Header Copyright notices updated to 2024. - Minimal white space cleanup (trailing spaces automatically removed). - Note: `asmxygen.py` has a *large* amount of whitespace cleanup, due to incorrect line endings. git-svn-id: svn://kolibrios.org@10051 a494cfbc-eb01-0410-851d-a64ba20cac60
951 lines
29 KiB
PHP
951 lines
29 KiB
PHP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
|
|
;; Distributed under terms of the GNU General Public License ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;**********************************************************
|
|
; Direct work with floppy disk drive
|
|
;**********************************************************
|
|
; Source code author - Kulakov Vladimir Gennadievich.
|
|
; Adaptation and improvement - Mario79.
|
|
|
|
give_back_application_data_1:
|
|
mov esi, FDD_BUFF;FDD_DataBuffer ;0x40000
|
|
mov ecx, 128
|
|
cld
|
|
rep movsd
|
|
ret
|
|
|
|
take_data_from_application_1:
|
|
mov edi, FDD_BUFF;FDD_DataBuffer ;0x40000
|
|
mov ecx, 128
|
|
cld
|
|
rep movsd
|
|
ret
|
|
|
|
; Controller operations result codes (FDC_Status)
|
|
FDC_Normal = 0 ; normal finish
|
|
FDC_TimeOut = 1 ; time out error
|
|
FDC_DiskNotFound = 2 ; no disk in drive
|
|
FDC_TrackNotFound = 3 ; track not found
|
|
FDC_SectorNotFound = 4 ; sector not found
|
|
|
|
; Maximum values of the sector coordinates (specified
|
|
; values correspond to the parameters of the standard
|
|
; 3-inch 1.44 MB floppy disk)
|
|
MAX_Track = 79
|
|
MAX_Head = 1
|
|
MAX_Sector = 18
|
|
|
|
uglobal
|
|
; Timer tick counter
|
|
TickCounter dd ?
|
|
; Operation completion code with the floppy disk drive controller
|
|
FDC_Status DB ?
|
|
; Interrupt flag from floppy disk drive
|
|
FDD_IntFlag DB ?
|
|
; The moment of the beginning of the last operation with FDD
|
|
FDD_Time DD ?
|
|
; Drive number
|
|
FDD_Type db 0
|
|
; Sector coordinates
|
|
FDD_Track DB ?
|
|
FDD_Head DB ?
|
|
FDD_Sector DB ?
|
|
|
|
; Operation result block
|
|
FDC_ST0 DB ?
|
|
FDC_ST1 DB ?
|
|
FDC_ST2 DB ?
|
|
FDC_C DB ?
|
|
FDC_H DB ?
|
|
FDC_R DB ?
|
|
FDC_N DB ?
|
|
; Read operation repetition counter
|
|
ReadRepCounter DB ?
|
|
; Recalibration operation repetition counter
|
|
RecalRepCounter DB ?
|
|
endg
|
|
; Memory area for storing the readed sector
|
|
;FDD_DataBuffer: times 512 db 0 ;DB 512 DUP (?)
|
|
fdd_motor_status db 0
|
|
timer_fdd_motor dd 0
|
|
|
|
;**************************************
|
|
;* INITIALIZATION OF DMA MODE FOR FDD *
|
|
;**************************************
|
|
Init_FDC_DMA:
|
|
pushad
|
|
mov al, 0
|
|
out 0x0c, al; reset the flip-flop to a known state.
|
|
mov al, 6 ; mask channel 2 so we can reprogram it.
|
|
out 0x0a, al
|
|
mov al, [dmamode]; 0x46 -> Read from floppy - 0x4A Write to floppy
|
|
out 0x0b, al
|
|
mov al, 0
|
|
out 0x0c, al; reset the flip-flop to a known state.
|
|
mov eax, 0xD000
|
|
out 0x04, al; set the channel 2 starting address to 0
|
|
shr eax, 8
|
|
out 0x04, al
|
|
shr eax, 8
|
|
out 0x81, al
|
|
mov al, 0
|
|
out 0x0c, al; reset flip-flop
|
|
mov al, 0xff;set count (actual size -1)
|
|
out 0x5, al
|
|
mov al, 0x1;[dmasize] ;(0x1ff = 511 / 0x23ff =9215)
|
|
out 0x5, al
|
|
mov al, 2
|
|
out 0xa, al
|
|
popad
|
|
ret
|
|
|
|
;***********************************
|
|
;* WRITE BYTE TO FDC DATA PORT *
|
|
;* Parameters: *
|
|
;* AL - byte to write. *
|
|
;***********************************
|
|
FDCDataOutput:
|
|
; DEBUGF 1,'K : FDCDataOutput(%x)',al
|
|
; pusha
|
|
push eax ecx edx
|
|
mov AH, AL ; remember byte to AH
|
|
; Reset controller state variable
|
|
mov [FDC_Status], FDC_Normal
|
|
; Check the readiness of the controller to receive data
|
|
mov DX, 3F4h ; (FDC state port)
|
|
mov ecx, 0x10000 ; set timeout counter
|
|
@@TestRS:
|
|
in AL, DX ; read the RS register
|
|
and AL, 0C0h ; get digits 6 and 7
|
|
cmp AL, 80h ; check digits 6 and 7
|
|
je @@OutByteToFDC
|
|
loop @@TestRS
|
|
; Time out error
|
|
; DEBUGF 1,' timeout\n'
|
|
mov [FDC_Status], FDC_TimeOut
|
|
jmp @@End_5
|
|
; Write byte to data port
|
|
@@OutByteToFDC:
|
|
inc DX
|
|
mov AL, AH
|
|
out DX, AL
|
|
; DEBUGF 1,' ok\n'
|
|
@@End_5:
|
|
; popa
|
|
pop edx ecx eax
|
|
ret
|
|
|
|
;******************************************
|
|
;* READ BYTE FROM FDC DATA PORT *
|
|
;* Procedure doesnt have input params. *
|
|
;* Output : *
|
|
;* AL - byte read. *
|
|
;******************************************
|
|
FDCDataInput:
|
|
push ECX
|
|
push DX
|
|
; Reset controller state variable
|
|
mov [FDC_Status], FDC_Normal
|
|
; Check the readiness of the controller to receive data
|
|
mov DX, 3F4h ;(FDC state port)
|
|
mov ecx, 0x10000 ; set timeout counter
|
|
@@TestRS_1:
|
|
in AL, DX ; read the RS register
|
|
and AL, 0C0h ; get digits 6 and 7
|
|
cmp AL, 0C0h ; check digits 6 and 7
|
|
je @@GetByteFromFDC
|
|
loop @@TestRS_1
|
|
; Time out error
|
|
; DEBUGF 1,'K : FDCDataInput: timeout\n'
|
|
mov [FDC_Status], FDC_TimeOut
|
|
jmp @@End_6
|
|
; Get byte from data port
|
|
@@GetByteFromFDC:
|
|
inc DX
|
|
in AL, DX
|
|
; DEBUGF 1,'K : FDCDataInput: %x\n',al
|
|
@@End_6:
|
|
pop DX
|
|
pop ECX
|
|
ret
|
|
|
|
;*********************************************
|
|
;* FDC INTERRUPT HANDLER *
|
|
;*********************************************
|
|
FDCInterrupt:
|
|
; dbgstr 'FDCInterrupt'
|
|
; Set the interrupt flag
|
|
mov [FDD_IntFlag], 1
|
|
mov al, 1
|
|
ret
|
|
|
|
;*******************************************
|
|
;* WAIT FOR INTERRUPT FROM FDC *
|
|
;*******************************************
|
|
WaitFDCInterrupt:
|
|
pusha
|
|
; Reset operation status byte
|
|
mov [FDC_Status], FDC_Normal
|
|
; Zero out the tick counter
|
|
mov eax, [timer_ticks]
|
|
mov [TickCounter], eax
|
|
; Wait for the floppy disk interrupt flag to be set
|
|
@@TestRS_2:
|
|
call change_task
|
|
cmp [FDD_IntFlag], 0
|
|
jnz @@End_7 ; interrupt occured
|
|
mov eax, [timer_ticks]
|
|
sub eax, [TickCounter]
|
|
cmp eax, 200;50 ;25 ;5 ; wait 5 ticks
|
|
jb @@TestRS_2
|
|
; jl @@TestRS_2
|
|
; Time out error
|
|
; dbgstr 'WaitFDCInterrupt: timeout'
|
|
mov [FDC_Status], FDC_TimeOut
|
|
@@End_7:
|
|
popa
|
|
ret
|
|
|
|
;***********************************
|
|
;* Turn on the motor of drive "A:" *
|
|
;***********************************
|
|
FDDMotorON:
|
|
; dbgstr 'FDDMotorON'
|
|
pusha
|
|
; cmp [fdd_motor_status],1
|
|
; je fdd_motor_on
|
|
mov al, [flp_number]
|
|
cmp [fdd_motor_status], al
|
|
je fdd_motor_on
|
|
; Reset the FDD controller
|
|
mov DX, 3F2h ; motor control port
|
|
mov AL, 0
|
|
out DX, AL
|
|
; Select and turn on the drive motor
|
|
cmp [flp_number], 1
|
|
jne FDDMotorON_B
|
|
; call FDDMotorOFF_B
|
|
mov AL, 1Ch ; Floppy A
|
|
jmp FDDMotorON_1
|
|
FDDMotorON_B:
|
|
; call FDDMotorOFF_A
|
|
mov AL, 2Dh ; Floppy B
|
|
FDDMotorON_1:
|
|
out DX, AL
|
|
; Zero out the tick counter
|
|
mov eax, [timer_ticks]
|
|
mov [TickCounter], eax
|
|
; wait 0.5 s
|
|
@@dT:
|
|
call change_task
|
|
mov eax, [timer_ticks]
|
|
sub eax, [TickCounter]
|
|
cmp eax, 50 ;10
|
|
jb @@dT
|
|
; Read results of RESET command
|
|
push 4
|
|
; DEBUGF 1,'K : floppy reset results:'
|
|
@@:
|
|
mov al, 8
|
|
call FDCDataOutput
|
|
call FDCDataInput
|
|
; DEBUGF 1,' %x',al
|
|
call FDCDataInput
|
|
; DEBUGF 1,' %x',al
|
|
dec dword [esp]
|
|
jnz @b
|
|
; DEBUGF 1,'\n'
|
|
pop eax
|
|
cmp [flp_number], 1
|
|
jne fdd_motor_on_B
|
|
mov [fdd_motor_status], 1
|
|
jmp fdd_motor_on
|
|
fdd_motor_on_B:
|
|
mov [fdd_motor_status], 2
|
|
fdd_motor_on:
|
|
call save_timer_fdd_motor
|
|
popa
|
|
ret
|
|
|
|
;*****************************************
|
|
;* SAVING TIME STAMP *
|
|
;*****************************************
|
|
save_timer_fdd_motor:
|
|
mov eax, [timer_ticks]
|
|
mov [timer_fdd_motor], eax
|
|
ret
|
|
|
|
;*****************************************
|
|
;* CHECK THE MOTOR SHUTDOWN DELAY *
|
|
;*****************************************
|
|
proc check_fdd_motor_status_has_work?
|
|
cmp [fdd_motor_status], 0
|
|
jz .no
|
|
mov eax, [timer_ticks]
|
|
sub eax, [timer_fdd_motor]
|
|
cmp eax, 500
|
|
jb .no
|
|
.yes:
|
|
xor eax, eax
|
|
inc eax
|
|
ret
|
|
.no:
|
|
xor eax, eax
|
|
ret
|
|
endp
|
|
|
|
align 4
|
|
check_fdd_motor_status:
|
|
cmp [fdd_motor_status], 0
|
|
je end_check_fdd_motor_status_1
|
|
mov eax, [timer_ticks]
|
|
sub eax, [timer_fdd_motor]
|
|
cmp eax, 500
|
|
jb end_check_fdd_motor_status
|
|
call FDDMotorOFF
|
|
mov [fdd_motor_status], 0
|
|
end_check_fdd_motor_status_1:
|
|
end_check_fdd_motor_status:
|
|
ret
|
|
|
|
;**********************************
|
|
;* TURN OFF MOTOR OF DRIVE *
|
|
;**********************************
|
|
FDDMotorOFF:
|
|
; dbgstr 'FDDMotorOFF'
|
|
push AX
|
|
push DX
|
|
cmp [flp_number], 1
|
|
jne FDDMotorOFF_1
|
|
call FDDMotorOFF_A
|
|
jmp FDDMotorOFF_2
|
|
FDDMotorOFF_1:
|
|
call FDDMotorOFF_B
|
|
FDDMotorOFF_2:
|
|
pop DX
|
|
pop AX
|
|
; clearing caching flags due to information obsolescence
|
|
or [floppy_media_flags+0], FLOPPY_MEDIA_NEED_RESCAN
|
|
or [floppy_media_flags+1], FLOPPY_MEDIA_NEED_RESCAN
|
|
ret
|
|
|
|
FDDMotorOFF_A:
|
|
mov DX, 3F2h ; motor control port
|
|
mov AL, 0Ch ; Floppy A
|
|
out DX, AL
|
|
ret
|
|
|
|
FDDMotorOFF_B:
|
|
mov DX, 3F2h ; motor control port
|
|
mov AL, 5h ; Floppy B
|
|
out DX, AL
|
|
ret
|
|
|
|
;*******************************
|
|
;* RECALIBRATE DRIVE "A:" *
|
|
;*******************************
|
|
RecalibrateFDD:
|
|
; dbgstr 'RecalibrateFDD'
|
|
pusha
|
|
call save_timer_fdd_motor
|
|
; Clear the interrupt flag
|
|
mov [FDD_IntFlag], 0
|
|
; Send the "Recalibration" command
|
|
mov AL, 07h
|
|
call FDCDataOutput
|
|
mov AL, [flp_number]
|
|
dec AL
|
|
call FDCDataOutput
|
|
; Wait for the operation to complete
|
|
call WaitFDCInterrupt
|
|
cmp [FDC_Status], 0
|
|
jne .fail
|
|
; Read results of RECALIBRATE command
|
|
; DEBUGF 1,'K : floppy recalibrate results:'
|
|
mov al, 8
|
|
call FDCDataOutput
|
|
call FDCDataInput
|
|
push eax
|
|
; DEBUGF 1,' %x',al
|
|
call FDCDataInput
|
|
; DEBUGF 1,' %x',al
|
|
; DEBUGF 1,'\n'
|
|
pop eax
|
|
test al, 0xC0
|
|
jz @f
|
|
mov [FDC_Status], FDC_DiskNotFound
|
|
@@:
|
|
.fail:
|
|
call save_timer_fdd_motor
|
|
popa
|
|
ret
|
|
|
|
;*****************************************************
|
|
;* TRACK SEARCH *
|
|
;* Parameters are passed through global variables: *
|
|
;* FDD_Track - track number (0-79); *
|
|
;* FDD_Head - head number (0-1). *
|
|
;* Result of operation is written to FDC_Status. *
|
|
;*****************************************************
|
|
SeekTrack:
|
|
; dbgstr 'SeekTrack'
|
|
pusha
|
|
call save_timer_fdd_motor
|
|
; Clear the interrupt flag
|
|
mov [FDD_IntFlag], 0
|
|
; Send "Search" command
|
|
mov AL, 0Fh
|
|
call FDCDataOutput
|
|
; Send head / drive number byte
|
|
mov AL, [FDD_Head]
|
|
shl AL, 2
|
|
call FDCDataOutput
|
|
; Send track number byte
|
|
mov AL, [FDD_Track]
|
|
call FDCDataOutput
|
|
; Wait for the operation to complete
|
|
call WaitFDCInterrupt
|
|
cmp [FDC_Status], FDC_Normal
|
|
jne @@Exit
|
|
; Save search result
|
|
mov AL, 08h
|
|
call FDCDataOutput
|
|
call FDCDataInput
|
|
mov [FDC_ST0], AL
|
|
call FDCDataInput
|
|
mov [FDC_C], AL
|
|
; Check search result
|
|
; Is search finished?
|
|
test [FDC_ST0], 100000b
|
|
je @@Err
|
|
; Is the specified track found?
|
|
mov AL, [FDC_C]
|
|
cmp AL, [FDD_Track]
|
|
jne @@Err
|
|
; Does the head number match the specified one?
|
|
; The H bit (Head Address) in ST0 will always return a "0" (c) 82077AA datasheet,
|
|
; description of SEEK command. So we can not verify the proper head.
|
|
; mov AL, [FDC_ST0]
|
|
; and AL, 100b
|
|
; shr AL, 2
|
|
; cmp AL, [FDD_Head]
|
|
; jne @@Err
|
|
; Operation completed successfully
|
|
; dbgstr 'SeekTrack: FDC_Normal'
|
|
mov [FDC_Status], FDC_Normal
|
|
jmp @@Exit
|
|
@@Err: ; Track not found
|
|
; dbgstr 'SeekTrack: FDC_TrackNotFound'
|
|
mov [FDC_Status], FDC_TrackNotFound
|
|
@@Exit:
|
|
call save_timer_fdd_motor
|
|
popa
|
|
ret
|
|
|
|
;*******************************************************
|
|
;* READING A DATA SECTOR *
|
|
;* Parameters are passed through global variables: *
|
|
;* FDD_Track - track number (0-79); *
|
|
;* FDD_Head - head number (0-1); *
|
|
;* FDD_Sector - sector number (1-18). *
|
|
;* Result of operation is written to FDC_Status. *
|
|
;* If the read operation is successful, the contents *
|
|
;* of the sector will be written to FDD_DataBuffer. *
|
|
;*******************************************************
|
|
ReadSector:
|
|
; dbgstr 'ReadSector'
|
|
pushad
|
|
call save_timer_fdd_motor
|
|
; Clear the interrupt flag
|
|
mov [FDD_IntFlag], 0
|
|
; Set transmit speed to 500 Kb / s
|
|
mov AX, 0
|
|
mov DX, 03F7h
|
|
out DX, AL
|
|
; Initialize the DMA channel
|
|
mov [dmamode], 0x46
|
|
call Init_FDC_DMA
|
|
; Send "Data read" command
|
|
mov AL, 0E6h ; reading in multi-track mode
|
|
call FDCDataOutput
|
|
mov AL, [FDD_Head]
|
|
shl AL, 2
|
|
or AL, [flp_number]
|
|
dec AL
|
|
call FDCDataOutput
|
|
mov AL, [FDD_Track]
|
|
call FDCDataOutput
|
|
mov AL, [FDD_Head]
|
|
call FDCDataOutput
|
|
mov AL, [FDD_Sector]
|
|
call FDCDataOutput
|
|
mov AL, 2 ; sector size code (512 byte)
|
|
call FDCDataOutput
|
|
mov AL, 18 ;+1; 3Fh ;number of sectors per track
|
|
call FDCDataOutput
|
|
mov AL, 1Bh ; GPL value
|
|
call FDCDataOutput
|
|
mov AL, 0FFh; DTL value
|
|
call FDCDataOutput
|
|
; Waiting for an interrupt at the end of the operation
|
|
call WaitFDCInterrupt
|
|
cmp [FDC_Status], FDC_Normal
|
|
jne @@Exit_1
|
|
; Read the operation completion status
|
|
call GetStatusInfo
|
|
test [FDC_ST0], 11011000b
|
|
jnz @@Err_1
|
|
; dbgstr 'ReadSector: FDC_Normal'
|
|
mov [FDC_Status], FDC_Normal
|
|
jmp @@Exit_1
|
|
@@Err_1:
|
|
; dbgstr 'ReadSector: FDC_SectorNotFound'
|
|
mov [FDC_Status], FDC_SectorNotFound
|
|
@@Exit_1:
|
|
call save_timer_fdd_motor
|
|
popad
|
|
ret
|
|
|
|
;*******************************************************
|
|
;* READ SECTOR (WITH RETRY OF OPERATION ON FAILURE) *
|
|
;* Parameters are passed through global variables: *
|
|
;* FDD_Track - track number (0-79); *
|
|
;* FDD_Head - head number (0-1); *
|
|
;* FDD_Sector - sector number (1-18). *
|
|
;* Result of operation is written to FDC_Status. *
|
|
;* If the read operation is successful, the contents *
|
|
;* of the sector will be written to FDD_DataBuffer. *
|
|
;*******************************************************
|
|
ReadSectWithRetr:
|
|
pusha
|
|
; Reset the recalibration repetition counter
|
|
mov [RecalRepCounter], 0
|
|
@@TryAgain:
|
|
; Reset the read operation retry counter
|
|
mov [ReadRepCounter], 0
|
|
@@ReadSector_1:
|
|
call ReadSector
|
|
cmp [FDC_Status], 0
|
|
je @@Exit_2
|
|
cmp [FDC_Status], 1
|
|
je @@Err_3
|
|
; Three times repeat reading
|
|
inc [ReadRepCounter]
|
|
cmp [ReadRepCounter], 3
|
|
jb @@ReadSector_1
|
|
; Three times repeat recalibration
|
|
call RecalibrateFDD
|
|
call SeekTrack
|
|
inc [RecalRepCounter]
|
|
cmp [RecalRepCounter], 3
|
|
jb @@TryAgain
|
|
@@Exit_2:
|
|
popa
|
|
ret
|
|
@@Err_3:
|
|
popa
|
|
ret
|
|
|
|
;*******************************************************
|
|
;* WRITE DATA SECTOR *
|
|
;* Parameters are passed through global variables: *
|
|
;* FDD_Track - track number (0-79); *
|
|
;* FDD_Head - head number (0-1); *
|
|
;* FDD_Sector - sector number (1-18). *
|
|
;* Result of operation is written to FDC_Status. *
|
|
;* If the write operation is successful, the contents *
|
|
;* of FDD_DataBuffer will be written to the sector *
|
|
;*******************************************************
|
|
WriteSector:
|
|
; dbgstr 'WriteSector'
|
|
pushad
|
|
call save_timer_fdd_motor
|
|
; Clear the interrupt flag
|
|
mov [FDD_IntFlag], 0
|
|
; Set transmit speed to 500 Kb / s
|
|
mov AX, 0
|
|
mov DX, 03F7h
|
|
out DX, AL
|
|
; Initialize the DMA channel
|
|
mov [dmamode], 0x4A
|
|
call Init_FDC_DMA
|
|
; Send "Write data" command
|
|
mov AL, 0xC5 ;0x45 ; write in multi-track mode
|
|
call FDCDataOutput
|
|
mov AL, [FDD_Head]
|
|
shl AL, 2
|
|
or AL, [flp_number]
|
|
dec AL
|
|
call FDCDataOutput
|
|
mov AL, [FDD_Track]
|
|
call FDCDataOutput
|
|
mov AL, [FDD_Head]
|
|
call FDCDataOutput
|
|
mov AL, [FDD_Sector]
|
|
call FDCDataOutput
|
|
mov AL, 2 ; sector size code (512 bytes)
|
|
call FDCDataOutput
|
|
mov AL, 18; 3Fh ; sectors per track
|
|
call FDCDataOutput
|
|
mov AL, 1Bh ; GPL value
|
|
call FDCDataOutput
|
|
mov AL, 0FFh; DTL value
|
|
call FDCDataOutput
|
|
; Waiting for an interrupt at the end of the operation
|
|
call WaitFDCInterrupt
|
|
cmp [FDC_Status], FDC_Normal
|
|
jne @@Exit_3
|
|
; Reading the completion status of the operation
|
|
call GetStatusInfo
|
|
test [FDC_ST0], 11000000b ;11011000b
|
|
jnz @@Err_2
|
|
mov [FDC_Status], FDC_Normal
|
|
jmp @@Exit_3
|
|
@@Err_2:
|
|
mov [FDC_Status], FDC_SectorNotFound
|
|
@@Exit_3:
|
|
call save_timer_fdd_motor
|
|
popad
|
|
ret
|
|
|
|
;*******************************************************
|
|
;* WRITE SECTOR (WITH REPEAT ON FAILURE) *
|
|
;* Parameters are passed through global variables: *
|
|
;* FDD_Track - track number (0-79); *
|
|
;* FDD_Head - head number (0-1); *
|
|
;* FDD_Sector - sector number (1-18). *
|
|
;* Result of operation is written to FDC_Status. *
|
|
;* If the write operation is successful, the contents *
|
|
;* of FDD_DataBuffer will be written to the sector *
|
|
;*******************************************************
|
|
WriteSectWithRetr:
|
|
pusha
|
|
; Reset the recalibration repetition counter
|
|
mov [RecalRepCounter], 0
|
|
@@TryAgain_1:
|
|
; Reset the read operation retry counter
|
|
mov [ReadRepCounter], 0
|
|
@@WriteSector_1:
|
|
call WriteSector
|
|
cmp [FDC_Status], 0
|
|
je @@Exit_4
|
|
cmp [FDC_Status], 1
|
|
je @@Err_4
|
|
; Three times repeat writing
|
|
inc [ReadRepCounter]
|
|
cmp [ReadRepCounter], 3
|
|
jb @@WriteSector_1
|
|
; Three times repeat recalibration
|
|
call RecalibrateFDD
|
|
call SeekTrack
|
|
inc [RecalRepCounter]
|
|
cmp [RecalRepCounter], 3
|
|
jb @@TryAgain_1
|
|
@@Exit_4:
|
|
popa
|
|
ret
|
|
@@Err_4:
|
|
popa
|
|
ret
|
|
|
|
;*********************************************
|
|
;* GET INFORMATION ABOUT THE RESULT OF THE OPERATION
|
|
;*********************************************
|
|
GetStatusInfo:
|
|
push AX
|
|
call FDCDataInput
|
|
mov [FDC_ST0], AL
|
|
call FDCDataInput
|
|
mov [FDC_ST1], AL
|
|
call FDCDataInput
|
|
mov [FDC_ST2], AL
|
|
call FDCDataInput
|
|
mov [FDC_C], AL
|
|
call FDCDataInput
|
|
mov [FDC_H], AL
|
|
call FDCDataInput
|
|
mov [FDC_R], AL
|
|
call FDCDataInput
|
|
mov [FDC_N], AL
|
|
pop AX
|
|
ret
|
|
|
|
; Interface for disk subsystem.
|
|
; Assume fixed capacity for 1.44M.
|
|
FLOPPY_CAPACITY = 2880 ; in sectors
|
|
|
|
iglobal
|
|
align 4
|
|
floppy_functions:
|
|
dd .size
|
|
dd 0 ; no close() function
|
|
dd 0 ; no closemedia() function
|
|
dd floppy_querymedia
|
|
dd floppy_read
|
|
dd floppy_write
|
|
dd 0 ; no flush() function
|
|
dd 0 ; no adjust_cache_size() function
|
|
.size = $ - floppy_functions
|
|
endg
|
|
|
|
uglobal
|
|
floppy_media_flags rb 2
|
|
n_sector dd 0 ; temporary save for sector value
|
|
flp_number db 0 ; 1- Floppy A, 2-Floppy B
|
|
old_track db 0 ; old value track
|
|
flp_label rb 15*2 ; Label and ID of inserted floppy disk
|
|
align 4
|
|
; Hardware does not allow to work with two floppies in parallel,
|
|
; so there is one mutex guarding access to any floppy.
|
|
floppy_mutex MUTEX
|
|
endg
|
|
; Meaning of bits in floppy_media_flags
|
|
FLOPPY_MEDIA_PRESENT = 1 ; media was present when last asked
|
|
FLOPPY_MEDIA_NEED_RESCAN = 2 ; media was possibly changed, need to rescan
|
|
FLOPPY_MEDIA_LABEL_CHANGED = 4 ; temporary state
|
|
|
|
iglobal
|
|
floppy1_name db 'fd',0
|
|
floppy2_name db 'fd2',0
|
|
endg
|
|
|
|
; This function is called in boot process.
|
|
; It creates filesystems /fd and/or /fd2, if the system has one/two floppy drives.
|
|
proc floppy_init
|
|
mov ecx, floppy_mutex
|
|
call mutex_init
|
|
; First floppy is present if [DRIVE_DATA] and 0xF0 is nonzero.
|
|
test byte [DRIVE_DATA], 0xF0
|
|
jz .no1
|
|
stdcall disk_add, floppy_functions, floppy1_name, 1, DISK_NO_INSERT_NOTIFICATION
|
|
.no1:
|
|
; Second floppy is present if [DRIVE_DATA] and 0x0F is nonzero.
|
|
test byte [DRIVE_DATA], 0x0F
|
|
jz .no2
|
|
stdcall disk_add, floppy_functions, floppy2_name, 2, DISK_NO_INSERT_NOTIFICATION
|
|
.no2:
|
|
ret
|
|
endp
|
|
|
|
; Returns information about disk media.
|
|
; Floppy drives do not support insert notifications,
|
|
; DISK_NO_INSERT_NOTIFICATION is set,
|
|
; the disk subsystem calls this function before each filesystem operation.
|
|
; If the media has changed, return error for the first call as signal
|
|
; to finalize work with old media and the true geometry for the second call.
|
|
; Assume that media is (possibly) changed anytime when motor is off.
|
|
proc floppy_querymedia
|
|
virtual at esp+4
|
|
.userdata dd ?
|
|
.info dd ?
|
|
end virtual
|
|
; 1. Acquire the global lock.
|
|
mov ecx, floppy_mutex
|
|
call mutex_lock
|
|
mov edx, [.userdata] ; 1 for /fd, 2 for /fd2
|
|
; 2. If the media was reported and has been changed, forget it and report an error.
|
|
mov al, [floppy_media_flags+edx-1]
|
|
and al, FLOPPY_MEDIA_PRESENT + FLOPPY_MEDIA_NEED_RESCAN
|
|
cmp al, FLOPPY_MEDIA_PRESENT + FLOPPY_MEDIA_NEED_RESCAN
|
|
jnz .not_reported
|
|
.no_media:
|
|
mov [floppy_media_flags+edx-1], 0
|
|
.return_no_media:
|
|
mov ecx, floppy_mutex
|
|
call mutex_unlock
|
|
mov eax, DISK_STATUS_NO_MEDIA
|
|
retn 8
|
|
.not_reported:
|
|
; 3. If we are in the temporary state LABEL_CHANGED, this is the second call
|
|
; after intermediate DISK_STATUS_NO_MEDIA due to media change;
|
|
; clear the flag and return the current geometry without rereading the bootsector.
|
|
cmp [floppy_media_flags+edx-1], FLOPPY_MEDIA_LABEL_CHANGED
|
|
jz .report_geometry
|
|
; 4. Try to read the bootsector.
|
|
mov [flp_number], dl
|
|
mov [FDC_Status], 0
|
|
call floppy_read_bootsector
|
|
; 5. If reading bootsector failed, assume that media is not present.
|
|
mov edx, [.userdata]
|
|
cmp [FDC_Status], 0
|
|
jnz .no_media
|
|
; 6. Check whether the previous status is "present". If not, go to 10.
|
|
push esi edi
|
|
imul edi, edx, 15
|
|
add edi, flp_label-15
|
|
mov esi, FDD_BUFF+39
|
|
mov ecx, 15
|
|
test [floppy_media_flags+edx-1], FLOPPY_MEDIA_PRESENT
|
|
jz .set_label
|
|
; 7. Compare the old label with the current one.
|
|
rep cmpsb
|
|
; 8. If the label has not changed, go to 11.
|
|
jz .ok
|
|
; 9. If the label has changed, store it, enter temporary state LABEL_CHANGED
|
|
; and report DISK_STATUS_NO_MEDIA.
|
|
; dbgstr 'floppy label changed'
|
|
add esi, ecx
|
|
add edi, ecx
|
|
mov ecx, 15
|
|
sub esi, ecx
|
|
sub edi, ecx
|
|
rep movsb
|
|
mov [floppy_media_flags+edx-1], FLOPPY_MEDIA_LABEL_CHANGED
|
|
pop edi esi
|
|
jmp .return_no_media
|
|
.set_label:
|
|
; 10. The previous state was "not present". Copy the label.
|
|
rep movsb
|
|
.ok:
|
|
pop edi esi
|
|
.report_geometry:
|
|
; 11. Fill DISKMEDIAINFO structure.
|
|
mov ecx, [.info]
|
|
and [ecx+DISKMEDIAINFO.Flags], 0
|
|
mov [ecx+DISKMEDIAINFO.SectorSize], 512
|
|
mov dword [ecx+DISKMEDIAINFO.Capacity], FLOPPY_CAPACITY
|
|
and dword [ecx+DISKMEDIAINFO.Capacity+4], 0
|
|
; 12. Update state: media is present, data are actual.
|
|
mov [floppy_media_flags+edx-1], FLOPPY_MEDIA_PRESENT
|
|
; 13. Release the global lock and return successful status.
|
|
mov ecx, floppy_mutex
|
|
call mutex_unlock
|
|
xor eax, eax
|
|
retn 8
|
|
endp
|
|
|
|
proc floppy_read_bootsector
|
|
pushad
|
|
mov [FDD_Track], 0 ; Cylinder
|
|
mov [FDD_Head], 0 ; Head
|
|
mov [FDD_Sector], 1 ; Sector
|
|
call FDDMotorON
|
|
call RecalibrateFDD
|
|
cmp [FDC_Status], 0
|
|
jne .nothing
|
|
call SeekTrack
|
|
cmp [FDC_Status], 0
|
|
jne .nothing
|
|
call ReadSectWithRetr
|
|
.nothing:
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
read_chs_sector:
|
|
call calculate_chs
|
|
call ReadSectWithRetr
|
|
ret
|
|
|
|
save_chs_sector:
|
|
call calculate_chs
|
|
call WriteSectWithRetr
|
|
ret
|
|
|
|
calculate_chs:
|
|
mov bl, [FDD_Track]
|
|
mov [old_track], bl
|
|
mov ebx, 18
|
|
xor edx, edx
|
|
div ebx
|
|
inc edx
|
|
mov [FDD_Sector], dl
|
|
mov edx, eax
|
|
shr eax, 1
|
|
and edx, 1
|
|
mov [FDD_Track], al
|
|
mov [FDD_Head], dl
|
|
mov dl, [old_track]
|
|
cmp dl, [FDD_Track]
|
|
je no_seek_track_1
|
|
call SeekTrack
|
|
no_seek_track_1:
|
|
ret
|
|
|
|
; Writes one or more sectors to the device.
|
|
proc floppy_write
|
|
mov dl, 1
|
|
jmp floppy_read_write
|
|
endp
|
|
|
|
; Reads one or more sectors from the device.
|
|
proc floppy_read
|
|
mov dl, 0
|
|
endp
|
|
|
|
; Common part of floppy_read and floppy_write.
|
|
proc floppy_read_write userdata:dword, buffer:dword, start_sector:qword, numsectors_ptr:dword
|
|
virtual at ebp-8
|
|
.sectors_todo dd ?
|
|
.operation db ?
|
|
end virtual
|
|
push edx ; save operation code to [.operation]
|
|
; 1. Get number of sectors to read/write
|
|
; and zero number of sectors that were actually read/written.
|
|
mov eax, [numsectors_ptr]
|
|
push dword [eax] ; initialize [.sectors_todo]
|
|
and dword [eax], 0
|
|
push ebx esi edi ; save used registers to be stdcall
|
|
; 2. Acquire the global lock.
|
|
mov ecx, floppy_mutex
|
|
call mutex_lock
|
|
; 3. Set floppy number for this operation.
|
|
mov edx, [userdata]
|
|
mov [flp_number], dl
|
|
; 4. Read/write sector-by-sector.
|
|
.operation_loop:
|
|
; 4a. Check that the sector is inside the media.
|
|
cmp dword [start_sector+4], 0
|
|
jnz .end_of_media
|
|
mov eax, dword [start_sector]
|
|
cmp eax, FLOPPY_CAPACITY
|
|
jae .end_of_media
|
|
; 4b. For read operation, call read_chs_sector and then move data from FDD_BUFF to [buffer].
|
|
; For write operation, move data from [buffer] to FDD_BUFF and then call save_chs_sector.
|
|
cmp [.operation], 0
|
|
jz .read
|
|
mov esi, [buffer]
|
|
mov edi, FDD_BUFF
|
|
mov ecx, 512/4
|
|
rep movsd
|
|
mov [buffer], esi
|
|
call save_chs_sector
|
|
jmp @f
|
|
.read:
|
|
call read_chs_sector
|
|
mov esi, FDD_BUFF
|
|
mov edi, [buffer]
|
|
mov ecx, 512/4
|
|
rep movsd
|
|
mov [buffer], edi
|
|
@@:
|
|
; 4c. If there was an error, propagate it to the caller.
|
|
cmp [FDC_Status], 0
|
|
jnz .fail
|
|
; 4d. Otherwise, increment number of sectors processed and continue the loop.
|
|
mov eax, [numsectors_ptr]
|
|
inc dword [eax]
|
|
inc dword [start_sector]
|
|
dec [.sectors_todo]
|
|
jnz .operation_loop
|
|
; 5. Release the global lock and return with the correct status.
|
|
push 0
|
|
.return:
|
|
mov ecx, floppy_mutex
|
|
call mutex_unlock
|
|
pop eax
|
|
pop edi esi ebx ; restore used registers to be stdcall
|
|
ret ; this translates to leave/retn N and purges local variables
|
|
.fail:
|
|
push -1
|
|
jmp .return
|
|
.end_of_media:
|
|
push DISK_STATUS_END_OF_MEDIA
|
|
jmp .return
|
|
endp
|