Files
crawos/source/kernel/features/disk.asm
2025-12-30 22:52:30 +00:00

377 lines
8.3 KiB
NASM

; Reset the disk system using int 13h / AH = 00h
disk_reset:
pusha
stc
mov ah, 00h
int 13h
popa
ret
; ------------------------------------
; Load the root directory into memory (into disk_buffer which is 24000h)
disk_load_root:
pusha
mov ax, 19 ; First sector of root entry
call math_lba_to_chs ; Assigns ch, cl and dh the correct values
xor ax,ax ; clear ax so I can use it on next line
mov dl, [ebr_drive_number] ; Drive number
mov ah, 02h ; BIOS ah code for read disk sectors to memory
mov al, 0Ch ; Root directory has 12 sectors
mov si, root_buffer ; ES:BX should point to our buffer
mov bx, si
; repeat drive read 3 times (incase of random error)
mov di, 3 ; counter
; This is basically
; for di in range(3,1,1):
; We test to see if we can read the disk 3 times
.try_read_disk: ; Try read the floppy three times
; When the disk is tried to be read, the CF flag will be
; set if there's an error, so we just clear it to return it to
; the default state
stc ; sets the cf flag
int 13h
jnc .done_read ; jump if cf = 0
call disk_reset ; Reset drivers of disk
dec di ; di -= 1
test di, di ; if di = 0
jnz .try_read_disk
jmp .disk_error
.disk_error:
mov si, disk_read_fail
call os_print_string
popa
ret
.done_read:
popa
ret
; ---------------------------------------------
; Reads a certain number of sectors into memory
; IN
; ax = LBA
; es:bx = area to read to/write from
; [read_write_flag] = 02 or 03 for read or write
; dl = ebr drive number
; int 13h/ah = 02h/03h read/write disk sectors into memory
; al = number of sectors to read
; ah = read/write 02=read, 03=write IMPORTANT
; ch = cylinder number
; cl = sector number
; dh = head number
; dl = drive number
; es:bx = points to data buffer
disk_read_or_write:
push ax
push bx
push cx
push dx
push di
; cl = sector
; ch = cylinder
; dh = head
; dl = drive
call math_lba_to_chs ; Get the chs address
mov ah, [read_write_flag]
mov al, 1
; repeat drive read 3 times (incase of random error)
mov di, 3 ; counter
; This is basically
; for di in range(3,1,1):
; We test to see if we can read the disk 3 times
.retry:
; When the disk is tried to be read, the CF flag will be
; set if there's an error, so we just clear it to return it to
; the default state
stc ; sets the cf flag
int 13h
jnc .done_read ; jump if cf = 0
call .disk_reset ; Reset drivers of disk
dec di ; di -= 1
test di, di ; if di = 0
jnz .retry
.fail_disk_read:
pop di
pop dx
pop cx
pop bx
pop ax
ret
; int 13h / ah = 00h reset disk system
.disk_reset:
pusha
mov ah, 0 ; Reset drive
stc
int 13h
jc .fail_disk_read
popa
ret
.done_read:
pop di
pop dx
pop cx
pop bx
pop ax
ret
; -------------------------------------
; CLEAR DATA
; Uses the metadata buffer to determine the file size in the data buffer and clears it.
disk_clear_file_buffer:
pusha
mov si, metadata_buffer
mov cx, [si+28] ; MOve the filelength into the ax
shr cx, 1 ; Divide by 4 because we'll write over using double words
mov si, empty_word
lodsb ; Load the empty dword
mov di, file_buffer
.loop:
stosb
dec cx
cmp cx, 0
jbe .loop
.finish:
popa
ret
disk_clear_output_buffer:
pusha
mov cx, 0x7f0 ; Length of the output buffer
mov si, empty_word
lodsb ; Load the empty word into ax
mov di, output_buffer
.loop:
stosb ; Store empty dword in di
dec cx
cmp cx, 0
ja .loop
.finish:
popa
ret
; -------------------------------------
; IN
; si: filename
; OUT
; data_buffer: file contents
; TODO use predefined data for calculations
disk_load_file:
pusha
call os_string_length ; cl = string length
cmp cl, 11
ja .filename_too_long
mov di, fat12_file_name
call os_format_fat_filename
; Prepare values
mov si, root_buffer
xor bx,bx
.search_root:
mov di, fat12_file_name ; Move the name of the kernel into di
mov cx, 11 ; length of filenames in fat12
push si ; Preserve si
repe cmpsb ; Repeat a comparison of bytes between file name and current bytes until it finds a match
pop si ; Retrieve si
je .found_file
add si, 32 ; increment di to the next directory entry
inc bx
cmp bx, [bdb_dir_entries_count] ; Have we reached the number of directories that exist
jl .search_root ; Repeat search
jmp .file_not_found ; If the last dir has been searched, then there is no kernel
.found_file:
call disk_clear_file_buffer
mov di, metadata_buffer
mov cx, 32
.write_metadata:
; Write data to metadata buffer
lodsb
stosb
sub cx, 1
cmp cx, 0
jne .write_metadata
.read_kernel:
sub si, 32
mov ax, [si+28] ; File length
mov [file_length], ax
mov ax, [si+26] ; ax is now a pointer to the pointer to the file relative to the data segments :D
add ax, 1Fh
mov [file_cluster], ax
; Setup registers for disk read
mov si, file_buffer
mov bx, si
mov dl, [ebr_drive_number]
mov ch, 02h
mov [read_write_flag], ch ; READ
call disk_read_or_write ; Load file from disk into memory
jmp .done
.file_not_found:
mov si, file_not_found
call os_print_string
jmp .done
.filename_too_long:
mov si, too_long_filename
call os_print_string
jmp .done
.done:
popa
ret
; Write data to file
; takes the contents in the data buffer for a fixed
; number of characters definied by [file_length]
; and write it to the file that si points to
; It also must edit the fat entry data
disk_write_file:
pusha
; Check if file name is too long
call os_string_length ; cl = string length
cmp cl, 11
ja .filename_too_long
; Convert file name to a fat filename
mov di, fat12_file_name
call os_format_fat_filename
; Locate the file entry in memory
; Prepare values
mov si, root_buffer
xor bx,bx
.search_root:
mov di, fat12_file_name ; Move the name of the kernel into di
mov cx, 11 ; length of filenames in fat12
push si ; Preserve si
repe cmpsb ; Repeat a comparison of bytes between file name and current bytes until it finds a match
pop si ; Retrieve si
je .found_file
add si, 32 ; increment di to the next directory entry
inc bx
cmp bx, [bdb_dir_entries_count] ; Have we reached the number of directories that exist
jl .search_root ; Repeat search
jmp .file_not_found ; If the last dir has been searched, then there is no kernel
.found_file:
; Find where the file is on the disk
mov ax, [si+28] ; File length entry
mov [file_length], ax
mov ax, [si+26] ; ax is now a pointer to the pointer to the file relative to the data segments :D
add ax, 1Fh
mov [file_cluster], ax
; Setup registers for disk read
mov si, file_buffer
mov bx, si
mov dl, [ebr_drive_number]
mov ch, 03h
mov [read_write_flag], ch ; WRITE
call disk_read_or_write ; Load file from disk into memory
jmp .finish
; DI now points to a fat12 formatted file name
; Write to those sectors
; Update FAT entry
.file_not_found: ; TODO create a file if it's not found
mov si, file_not_found
call os_print_string
jmp .finish
.filename_too_long:
mov si, too_long_filename
call os_print_string
jmp .finish
.finish:
popa
ret
; TODO support long file names
; Store a list of the files in file_buffer
; in a human readable format
; OUT
; output_buffer: the list (string)
disk_list_contents:
pusha
mov si, root_buffer
mov di, output_buffer
.loop:
call string_unformat_fat_filename
mov cx, 000Ch
sub cx, [file_name_length]
add di, [file_name_length]
mov al, 20h
.space_loop:
stosb
dec cx
cmp cx, 0
jne .space_loop
.file_attribute:
add si, 11d
lodsb
push si
mov si, read_only
cmp al, 1h
je .add_string
mov si, hidden
cmp al, 2h
je .add_string
mov si, system
cmp al, 4h
je .add_string
mov si, volume
cmp al, 8h
je .add_string
mov si, directory
cmp al, 10h
je .add_string
mov si, archive
cmp al, 20h
je .add_string
.after_file_attributes:
pop si
mov al, 0Ah
stosb
add si, 20d
lodsb ; +1
dec si ; -1
cmp al, 0 ; You've come to the end of the root entries
je .finish
cmp al, 00E5h ; E5h is a marker that the Fat entry is available
je .finish
jmp .loop
.add_string:
.add_string_loop:
lodsb
cmp ax, 00h
je .after_file_attributes
stosb
jmp .add_string_loop
.finish:
mov cx, output_buffer
sub di, cx
mov [file_length], di
stosb
popa
ret