1 ; =============================================================================
2 ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems
3 ; Copyright (C) 2008-2012 Return Infinity -- see LICENSE.TXT
6 ; =============================================================================
13 ; -----------------------------------------------------------------------------
14 ; os_fat16_read_cluster -- Read a cluster from the FAT16 partition
15 ; IN: AX = Cluster # to read
16 ; RDI = Memory location to store at least 32KB
17 ; OUT: AX = Next cluster in chain (0xFFFF if this was the last)
18 ; RDI = Points one byte after the last byte read
19 os_fat16_read_cluster:
25 and rax
, 0x000000000000FFFF ; Clear the top 48 bits
26 mov rbx
, rax
; Save the cluster number to be used later
28 cmp ax, 2 ; If less than 2 then bail out...
29 jl near os_fat16_read_cluster_bailout
; as clusters start at 2
31 ; Calculate the LBA address --- startingsector = (cluster-2) * clustersize + data_start
33 mov cl, byte [fat16_SectorsPerCluster
]
34 push rcx
; Save the number of sectors per cluster
36 imul
cx ; EAX now holds starting sector
37 add eax, dword [fat16_DataStart
] ; EAX now holds the sector where our cluster starts
38 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
40 pop rcx
; Restore the number of sectors per cluster
41 call readsectors
; Read one cluster of sectors
43 ; Calculate the next cluster
45 ; tint1 = Cluster / 256 <- Dump the remainder
46 ; sector_to_read = tint1 + ReservedSectors
47 ; tint2 = (Cluster - (tint1 * 256)) * 2
49 mov rdi
, secbuffer1
; Read to this temporary buffer
50 mov rsi
, rdi
; Copy buffer address to RSI
51 push rbx
; Save the original cluster value
52 shr rbx
, 8 ; Divide the cluster value by 256. Keep no remainder
53 movzx ax, [fat16_ReservedSectors
] ; First sector of the first FAT
54 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
55 add rax
, rbx
; Add the sector offset
58 pop rax
; Get our original cluster value back
59 shl rbx
, 8 ; Quick multiply by 256 (RBX was the sector offset in the FAT)
60 sub rax
, rbx
; RAX is now pointed to the offset within the sector
61 shl rax
, 1 ; Quickly multiply by 2 (since entries are 16-bit)
63 lodsw ; AX now holds the next cluster
66 jmp os_fat16_read_cluster_end
68 os_fat16_read_cluster_bailout:
71 os_fat16_read_cluster_end:
77 ; -----------------------------------------------------------------------------
80 ; -----------------------------------------------------------------------------
81 ; os_fat16_write_cluster -- Write a cluster to the FAT16 partition
82 ; IN: AX = Cluster # to write to
83 ; RSI = Memory location of data to write
84 ; OUT: AX = Next cluster in the chain (set to 0xFFFF if this was the last cluster of the chain)
85 ; RSI = Points one byte after the last byte written
86 os_fat16_write_cluster:
92 and rax
, 0x000000000000FFFF ; Clear the top 48 bits
93 mov rbx
, rax
; Save the cluster number to be used later
95 cmp ax, 2 ; If less than 2 then bail out...
96 jl near os_fat16_write_cluster_bailout
; as clusters start at 2
98 ; Calculate the LBA address --- startingsector = (cluster-2) * clustersize + data_start
100 mov cl, byte [fat16_SectorsPerCluster
]
101 push rcx
; Save the number of sectors per cluster
103 imul
cx ; EAX now holds starting sector
104 add eax, dword [fat16_DataStart
] ; EAX now holds the sector where our cluster starts
105 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
107 pop rcx
; Restore the number of sectors per cluster
110 ; Calculate the next cluster
112 mov rdi
, secbuffer1
; Read to this temporary buffer
113 mov rsi
, rdi
; Copy buffer address to RSI
114 push rbx
; Save the original cluster value
115 shr rbx
, 8 ; Divide the cluster value by 256. Keep no remainder
116 movzx ax, [fat16_ReservedSectors
] ; First sector of the first FAT
117 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
118 add rax
, rbx
; Add the sector offset
121 pop rax
; Get our original cluster value back
122 shl rbx
, 8 ; Quick multiply by 256 (RBX was the sector offset in the FAT)
123 sub rax
, rbx
; RAX is now pointed to the offset within the sector
124 shl rax
, 1 ; Quickly multiply by 2 (since entries are 16-bit)
126 lodsw ; AX now holds the next cluster
129 jmp os_fat16_write_cluster_done
131 os_fat16_write_cluster_bailout:
134 os_fat16_write_cluster_done:
140 ; -----------------------------------------------------------------------------
143 ; -----------------------------------------------------------------------------
144 ; os_fat16_find_file -- Search for a file name and return the starting cluster
145 ; IN: RSI = Pointer to file name, must be in 'FILENAMEEXT' format
146 ; OUT: AX = Staring cluster
148 ; Carry set if not found. If carry is set then ignore value in AX
157 mov eax, [fat16_RootStart
] ; eax points to the first sector of the root
158 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
159 mov rdx
, rax
; Save the sector value
161 os_fat16_find_file_read_sector:
167 mov rbx
, 16 ; Each record is 32 bytes. 512 (bytes per sector) / 32 = 16
169 os_fat16_find_file_next_entry:
170 cmp byte [rdi
], 0x00 ; end of records
171 je os_fat16_find_file_notfound
177 mov ax, [rdi
+15] ; AX now holds the starting cluster # of the file we just looked at
178 mov ecx, [rdi
+17] ; ECX now holds the size of the file in bytes
179 jz os_fat16_find_file_done
; The file was found. Note that rdi now is at dirent+11
185 jne os_fat16_find_file_next_entry
187 ; At this point we have read though one sector of file names. We have not found the file we are looking for and have not reached the end of the table. Load the next sector.
191 jmp os_fat16_find_file_read_sector
193 os_fat16_find_file_notfound:
197 os_fat16_find_file_done:
198 cmp ax, 0x0000 ; BUG HERE
199 jne wut
; Carry is not being set properly in this function
207 ; -----------------------------------------------------------------------------
210 ; -----------------------------------------------------------------------------
211 ; os_fat16_get_file_list -- Generate a list of files on disk
212 ; IN: RDI = location to store list
213 ; OUT: RDI = pointer to end of list
214 os_fat16_get_file_list:
222 mov rsi
, dir_title_string
223 call os_string_length
224 call os_string_copy
; Copy the header
229 mov ebx, [fat16_RootStart
] ; ebx points to the first sector of the root
230 add ebx, [fat16_PartitionOffset
] ; Add the offset to the partition
232 jmp os_fat16_get_file_list_read_sector
234 os_fat16_get_file_list_next_sector:
237 os_fat16_get_file_list_read_sector:
246 ; RDI = location of string
247 ; RSI = buffer that contains the cluster
250 os_fat16_get_file_list_read:
251 cmp rsi
, hdbuffer1
+512
252 je os_fat16_get_file_list_next_sector
253 cmp byte [rsi
], 0x00 ; end of records
254 je os_fat16_get_file_list_done
255 cmp byte [rsi
], 0xE5 ; unused record
256 je os_fat16_get_file_list_skip
258 mov al, [rsi
+ 8] ; Grab the attribute byte
259 bt ax, 5 ; check if bit 3 is set (volume label)
260 jc os_fat16_get_file_list_skip
; if so skip the entry
261 mov al, [rsi
+ 11] ; Grab the attribute byte
262 cmp al, 0x0F ; Check if it is a LFN entry
263 je os_fat16_get_file_list_skip
; if so skip the entry
268 os_fat16_get_file_list_copy:
273 jne os_fat16_get_file_list_copy
275 mov al, ' ' ; Store a space as the separtator
285 mov al, ' ' ; Store a space as the separtator
289 call os_int_to_string
294 os_fat16_get_file_list_skip:
296 jmp os_fat16_get_file_list_read
298 os_fat16_get_file_list_done:
309 dir_title_string: db "Name Ext Size", 13, "====================", 13, 0
310 ; -----------------------------------------------------------------------------
313 ; -----------------------------------------------------------------------------
314 ; os_fat16_file_read -- Read a file from disk into memory
315 ; IN: RSI = Address of filename string
316 ; RDI = Memory location where file will be loaded to
317 ; OUT: Carry clear on success, set if file was not found or error occured
321 push rcx
; Used by os_fat16_find_file
324 ; Convert the file name to FAT format
325 push rdi
; Save the memory address
326 mov rdi
, os_fat16_file_read_string
327 call os_fat16_filename_convert
; Convert the filename to the proper FAT format
329 pop rdi
; Grab the memory address
330 jc os_fat16_file_read_done
; If Carry is set then the filename could not be converted
332 ; Check to see if the file exists
333 call os_fat16_find_file
; Fuction will return the starting cluster value in AX or carry set if not found
334 jc os_fat16_file_read_done
; If Carry is clear then the file exists. AX is set to the starting cluster
336 os_fat16_file_read_read:
337 call os_fat16_read_cluster
; Store cluster in memory. AX is set to the next cluster
338 cmp ax, 0xFFFF ; 0xFFFF is the FAT end of file marker
339 jne os_fat16_file_read_read
; Are there more clusters? If so then read again.. if not fall through
342 os_fat16_file_read_done:
349 os_fat16_file_read_string times
13 db 0
350 ; -----------------------------------------------------------------------------
353 ; -----------------------------------------------------------------------------
354 ; os_fat16_file_write -- Write a file to the hard disk
355 ; IN: RSI = Address of data in memory
356 ; RDI = File name to write
357 ; RCX = number of bytes to write
358 ; OUT: Carry clear on success, set on failure
365 mov [memory_address
], rsi
; Save the memory address
367 ; Convert the file name to FAT format
368 mov rsi
, rdi
; Move the file name address into RSI
369 mov rdi
, os_fat16_file_write_string
370 call os_fat16_filename_convert
; Convert the filename to the proper FAT format
371 jc os_fat16_file_write_done
; Fail (Invalid file name)
373 ; Check to see if a file already exists with the same name
374 mov rsi
, os_fat16_file_write_string
376 call os_fat16_find_file
; Returns the starting cluster in AX or carry set if it doesn't exist
378 jc os_fat16_file_write_create
; Jump if the file doesn't exist (No need to delete it)
379 jmp os_fat16_file_write_done
; call os_fat16_file_delete
381 ; At this point the file doesn't exist so create it.
382 os_fat16_file_write_create:
384 call os_fat16_file_create
385 jc os_fat16_file_write_done
; Fail (Couldn't create the file)
386 call os_fat16_find_file
; Call this to get the starting cluster
387 jc os_fat16_file_write_done
; Fail (File was supposed to be created but wasn't)
389 ; We are ready to start writing. First cluster is in AX
390 mov rsi
, [memory_address
]
391 os_fat16_file_write_write:
392 call os_fat16_write_cluster
394 jne os_fat16_file_write_write
397 os_fat16_file_write_done:
404 os_fat16_file_write_string times
13 db 0
405 memory_address
dq 0x0000000000000000
406 ; -----------------------------------------------------------------------------
409 ; -----------------------------------------------------------------------------
410 ; os_fat16_file_create -- Create a file on the hard disk
411 ; IN: RSI = Pointer to file name, must be in FAT 'FILENAMEEXT' format
413 ; OUT: Carry clear on success, set on failure
414 ; Note: This function pre-allocates all clusters required for the size of the file
415 os_fat16_file_create:
423 clc ; Clear the carry flag. It will be set if there is an error
425 mov [filesize
], ecx ; Save file size for later
428 ; Check to see if a file already exists with the same name
429 ; call os_fat16_find_file
430 ; jc os_fat16_file_create_fail ; Fail (File already exists)
432 ; How many clusters will we need?
436 mov bl, byte [fat16_SectorsPerCluster
]
437 shl rbx
, 9 ; Multiply by 512 to get bytes per cluster
440 jg add_a_bit
; If there's a remainder, we need another cluster
445 mov rcx
, rax
; RCX holds number of clusters that are needed
447 ; Allocate all of the clusters required for the amount of bytes we are writting.
449 mov ax, [fat16_ReservedSectors
] ; First sector of the first FAT
450 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
457 xor rdx
, rdx
; cluster we are currently at
458 xor rbx
, rbx
; cluster marker
464 jne findfirstfreeclust
; Continue until we find a free cluster
466 mov [startcluster
], dx ; Save the starting cluster ID
478 jne findnextfreecluster
486 jne findnextfreecluster
491 ; push dx ; save the free cluster number
493 ; cmp rbx, rcx ; Have we found enough free clusters?
494 ; jne nextclust ; If not keep going, if yes fall through
495 ; At this point we have free cluster ID's on the stack
501 ; At this point we have a sector of FAT in hdbuffer0. A cluster has been marked in use but the sector is not written back to disk yet!
502 ; Save the sector # as we will write this to disk later
504 ; Load the first sector of the file info table
506 mov eax, [fat16_RootStart
] ; eax points to the first sector of the root
507 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
513 mov rcx
, 16 ; records / sector
517 cmp byte [rsi
], 0x00 ; Empty record
519 cmp byte [rsi
], 0xE5 ; Unused record
521 add rsi
, 32 ; Each record is 32 bytes
523 je os_fat16_file_create_fail
527 ; At this point RSI points to the start of the record
545 stosw ; Modified time
546 stosw ; Modified date
547 mov ax, [startcluster
]
552 ; At this point the updated file record is in memory at hdbuffer1
556 movzx ax, [fat16_ReservedSectors
] ; First sector of the first FAT
557 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
562 mov eax, [fat16_RootStart
] ; eax points to the first sector of the root
563 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
568 jmp os_fat16_file_create_done
570 os_fat16_file_create_fail:
574 os_fat16_file_create_done:
583 ; newfile_string times 13 db 0
584 startcluster
dw 0x0000
585 filesize
dd 0x00000000
586 filename
dq 0x0000000000000000
587 ; -----------------------------------------------------------------------------
590 ; -----------------------------------------------------------------------------
591 ; os_fat16_file_delete -- Delete a file from the hard disk
592 ; IN: RSI = File name to delete
593 ; OUT: Carry clear on success, set on failure
594 os_fat16_file_delete:
603 mov eax, [fat16_RootStart
] ; eax points to the first sector of the root
604 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
605 mov rdx
, rax
; Save the sector value
607 ; Convert the file name to FAT format
608 mov rdi
, os_fat16_file_delete_string
609 call os_fat16_filename_convert
; Convert the filename to the proper FAT format
610 jc os_fat16_file_delete_error
; Fail (Invalid file name)
613 ; Read through the root cluster (if file not found bail out)
614 os_fat16_file_delete_read_sector:
620 mov rbx
, 16 ; Each record is 32 bytes. 512 (bytes per sector) / 32 = 16
622 os_fat16_file_delete_next_entry:
623 cmp byte [rdi
], 0x00 ; end of records
624 je os_fat16_file_delete_error
630 mov ax, [rdi
+15] ; AX now holds the starting cluster # of the file we just looked at
631 jz os_fat16_file_delete_found
; The file was found. Note that rdi now is at dirent+11
633 add rdi
, byte 0x20 ; Great little trick here. Add 32 ...
634 and rdi
, byte -0x20 ; ... and then round backwards to a 32-byte barrier. Sets RDI to the start of the next record
637 jne os_fat16_file_delete_next_entry
639 ; At this point we have read though one sector of file names. We have not found the file we are looking for and have not reached the end of the table. Load the next sector.
640 add rdx
, 1 ; We are about to read the next sector so increment the counter
642 jmp os_fat16_file_delete_read_sector
644 ; Mark the file as deleted (set first byte of file name to 0xE5) and write the sector back to the drive
645 os_fat16_file_delete_found:
647 mov bx, ax ; Save the starting cluster value
648 and rdi
, byte -0x20 ; Round backward to get to the start of the record
649 mov al, 0xE5 ; Set the first character of the filename to this
652 mov rax
, rdx
; Retrieve the sector number
656 ; Follow cluster chain and set any cluster in the chain to 0x0000 (mark as free)
658 mov ax, [fat16_ReservedSectors
] ; First sector of the first FAT
659 add eax, [fat16_PartitionOffset
] ; Add the offset to the partition
660 mov rdx
, rax
; Save the sector value
666 os_fat16_file_delete_next_cluster:
668 mov ax, word [rsi
+rbx
]
669 mov [rsi
+rbx
], word 0x0000
672 jne os_fat16_file_delete_next_cluster
673 mov rax
, rdx
; Get the sector back. RSI already points to what we need
676 jmp os_fat16_file_delete_done
678 os_fat16_file_delete_error:
682 os_fat16_file_delete_done:
690 os_fat16_file_delete_string times
13 db 0
691 ; -----------------------------------------------------------------------------
694 ; -----------------------------------------------------------------------------
695 ; os_fat16_get_file_size -- Read a file from disk into memory
696 ; IN: RSI = Address of filename string
697 ; OUT: RCX = Size of file in bytes
698 ; Carry clear on success, set if file was not found or error occured
699 os_fat16_get_file_size:
705 ; Convert the file name to FAT format
706 mov rdi
, os_fat16_get_file_size_string
707 call os_fat16_filename_convert
; Convert the filename to the proper FAT format
709 jc os_fat16_file_read_done
; If Carry is set then the filename could not be converted
711 ; Check to see if the file exists
712 call os_fat16_find_file
; Fuction will return the starting cluster value in AX and size in ECX or carry set if not found
714 os_fat16_get_file_size_done:
720 os_fat16_get_file_size_string times
13 db 0
721 ; -----------------------------------------------------------------------------
724 ; -----------------------------------------------------------------------------
725 ; os_fat16_filename_convert -- Change 'test.er' into 'TEST ER ' as per FAT16
726 ; IN: RSI = filename string
727 ; RDI = location to store converted string (carry set if invalid)
728 ; OUT: All registers preserved
729 ; NOTE: Must have room for 12 bytes. 11 for the name and 1 for the NULL
730 ; Need fix for short extensions!
731 os_fat16_filename_convert:
739 mov rbx
, rdi
; Save the string destination address
740 call os_string_length
741 cmp rcx
, 12 ; Bigger than name + dot + extension?
742 jg os_fat16_filename_convert_failure
; Fail if so
744 je os_fat16_filename_convert_failure
; Similarly, fail if zero-char string
746 mov rdx
, rcx
; Store string length for now
748 os_fat16_filename_convert_copy_loop:
751 je os_fat16_filename_convert_extension_found
755 jg os_fat16_filename_convert_failure
; No extension found = wrong
756 jmp os_fat16_filename_convert_copy_loop
758 os_fat16_filename_convert_failure:
759 stc ; Set carry for failure
760 jmp os_fat16_filename_convert_done
762 os_fat16_filename_convert_extension_found:
764 je os_fat16_filename_convert_failure
; Fail if extension dot is first char
766 je os_fat16_filename_convert_do_extension
; Skip spaces if first bit is 8 chars
769 os_fat16_filename_convert_add_spaces:
773 jl os_fat16_filename_convert_add_spaces
775 os_fat16_filename_convert_do_extension: ; FIX THIS for cases where ext is less than 3 chars
782 mov byte [rdi
], 0 ; Zero-terminate filename
783 clc ; Clear carry for success
784 mov rsi
, rbx
; Get the start address of the desitination string
785 call os_string_uppercase
; Set it all to uppercase
787 os_fat16_filename_convert_done:
795 ; -----------------------------------------------------------------------------
798 ; =============================================================================