;Load a file named at SI as a string ending in a null to the offset BX ;and store the file size in CX and the error codes in AL: ; * AL = 0x0: Succesful load ; * AL = 0x1: Drive not found ; * AL = 0x2: Unable to read disk ; * AL = 0x4: File or command not found ; * AL = 0x8: Not enough memory loadf: ;Store BX, DX, SI, and DI in the stack push bx push dx push si push di ;Store the current drive and offset mov word [.pointer], bx mov dl, byte [drive] mov byte [.drive], dl ;Change the drive if needed ;Check for a drive specification cmp byte [si + 0x1], ":" jne .convert ;Get the BIOS equipment list int 0x11 ;Get the number of floppy drives times 0x6 shr ax, 0x1 and ax, 0x3 inc ax ;Set the loop and drive letter counters and the drive letter and number mov cx, ax mov di, driveletters mov al, [si] mov dl, 0x0 ;Check which drive to change to .checkdrive: cmp al, [di] je .contchdrive inc di cmp al, [di] je .contchdrive inc di inc dl loop .checkdrive ;Set AL to 0x1, CX to 0x0, and print an error message mov si, .driverrormsg mov ah, 0x2 int 0x21 mov al, 0x1 mov cx, 0x0 jmp .done ;Change the drive .contchdrive: mov [.drive], dl ;Move to the file name add si, 0x2 ;Set DI at .file and initialise it with spaces .convert: mov di, .file mov cx, 0xb mov al, 0x20 rep stosb sub di, 0xb ;Convert the main part of the file name into FAT format ;Set the length counter mov bl, 0x8 ;Load a character .nameloop: lodsb ;Check for a period cmp al, 0x2e je .initext ;Check for everything else and convert to upper case call .checkconv ;Load the next character jmp .nameloop ;Convert the extension into FAT format ;Set DI and the length counter for the extension .initext: mov bl, 0x3 mov di, .file+0x8 ;Load a character .extloop: lodsb ;Check for a period push ax cmp al, 0x2e je .error pop ax ;Check for everything else and convert to upper case call .checkconv ;Load the next character jmp .extloop ;Set AL to 0x4, CX to 0x0, and print an error message .error: pop ax mov si, .filerrormsg mov ah, 0x2 int 0x21 mov al, 0x4 mov cx, 0x0 jmp .done ;Find and load the file .load: pop ax ;Load the disk description table ;Set the source mov dl, [.drive] mov ch, 0x0 mov dh, 0x0 mov cl, 0x1 ;Set the destination mov si, buffer mov bx, si ;Set the size mov al, 0x1 ;Load the disk description table mov ah, 0x2 int 0x13 jnc .storevalues ;Set AL to 0x2, CX to 0x0, and print an error message mov si, .diskerrormsg mov ah, 0x2 int 0x21 mov al, 0x2 mov cx, 0x0 jmp .done ;Store the disk values used for the rest of the call .storevalues: mov ax, [buffer + 0xb] mov [.sectorsize], ax mov al, [buffer + 0xd] mov [.clustersize], al mov ax, [buffer + 0xe] mov [.reservedsectors], ax mov al, [buffer + 0x10] mov [.fats], al mov ax, [buffer + 0x11] mov [.rootentries], ax mov ax, [buffer + 0x16] mov [.sectorsperfat], ax mov ax, [buffer + 0x18] mov [.sectorspertrack], ax mov ax, [buffer + 0x1a] mov [.heads], ax ;Calculate and store variables not found in the BPB ;Start of the root mov ah, 0x0 mov al, [.fats] mul word [.sectorsperfat] add ax, [.reservedsectors] mov [.rootstart], ax ;Size of the root in sectors mov ax, [.rootentries] mov dx, 0x20 mul dx mov dx, 0x0 div word [.sectorsize] mov [.rootsectors], ax ;Start of data add ax, [.rootstart] mov [.datastart], ax ;Load the root ;Set the source mov ax, [.rootstart] ;Set the destination mov si, buffer mov bx, si ;Set the size mov cx, [.rootsectors] ;Store the source and the loop counter in the stack .loadrootsector: push ax push cx ;Set the source call .calcsource ;Set the size mov al, 0x1 ;Load a sector mov ah, 0x2 int 0x13 ;Load the loop counter and the source from the stack pop cx pop ax ;Set the next sector add ax, 0x1 add bx, word [.sectorsize] ;Load the next sector loop .loadrootsector ;Search the root for the file entry ;Set DI to the root mov di, buffer ;Set the number of root entries mov cx, word [.rootentries] ;Set the entry pointer mov ax, 0x0 ;Store the loop counter in the stack .search: push cx ;Check for the file entry mov si, .file mov cx, 0xb rep cmpsb je .checksize ;Set DI at the next entry add ax, 0x20 mov di, buffer add di, ax ;Load the loop counter from the stack pop cx ;Search the next entry loop .search ;Set AL to 0x4, CX to 0x0, and print an error message mov si, .filerrormsg mov ah, 0x2 int 0x21 mov al, 0x4 mov cx, 0x0 jmp .done ;Check and store the file size ;Load CX from the stack .checksize: pop cx ;Check for files larger than 64 KiB cmp word [di + 0x13], 0x0 jne .sizerror ;Get the cluster size in bytes mov ah, 0x0 mov al, [.clustersize] mul word [.sectorsize] mov bx, ax ;Store the file size mov ax, [di + 0x11] mov word [.size], ax ;Check for files smaller than 64 KiB but too large to fit into memory jc .sizerror dec ax mov dx, 0x0 div bx mul bx add ax, [.pointer] jnc .loadfat ;Set AL to 0x8, CX to 0x0, and print an error message .sizerror: mov si, .sizerrormsg mov ah, 0x2 int 0x21 mov al, 0x8 mov cx, 0x0 jmp .done ;Load the FAT .loadfat: ;Store the address of the first cluster mov ax, [di + 0xf] mov [.cluster], ax ;Set the source mov ax, [.reservedsectors] call .calcsource ;Set the destination mov bx, buffer ;Set the size mov ax, [.sectorsperfat] ;Load the FAT mov ah, 0x2 int 0x13 ;Load a cluster .loadcluster: ;Set the source mov ax, word [.cluster] sub ax, 0x2 mul byte [.clustersize] add ax, [.datastart] ;Set the destination mov bx, word [.pointer] ;Set the size mov ch, 0x0 mov cl, byte [.clustersize] ;Store the loop counter in the stack .loadsector: push cx ;Set the source call .calcsource ;Set the size push ax mov al, 0x1 ;Load a sector mov ah, 0x2 int 0x13 pop ax ;Set the next sector add ax, 0x1 add bx, word [.sectorsize] ;Load the loop counter from the stack pop cx ;Load the next sector loop .loadsector ;Calculate the next cluster ;Check if the cluster is even or odd mov ax, [.cluster] mov dx, 0x0 mov bx, 0x3 mul bx mov bx, 0x2 div bx mov si, buffer add si, ax mov ax, word [si] or dx, dx jz .even ;If the cluster is odd shift out the first four bits times 0x4 shr ax, 0x1 jmp .contcalc ;If the cluster is even mask out the final four bits .even: and ax, 0xfff .contcalc: ;Check for the file end cmp ax, 0xff8 jge .success ;Store the address of the next cluster mov word [.cluster], ax ;Set the destination of the next cluster mov ax, [.sectorsize] mul word [.clustersize] add word [.pointer], ax ;Load the next cluster jmp .loadcluster ;Set AL to 0x0 and load the file size to CX if the load was succesful .success: mov al, 0x0 mov cx, [.size] ;Clear the stack .done: ;Load DI, SI, DX, and BX from the stack pop di pop si pop dx pop bx ;Set AH to its initial value mov ah, 0x0 ;Return iret ;Data .drive db 0x0 .sectorsize dw 0x0 ;bytes .clustersize db 0x0 ;sectors .reservedsectors dw 0x0 .fats db 0x0 .rootentries dw 0x0 .sectorsperfat dw 0x0 .sectorspertrack dw 0x0 .heads dw 0x0 .rootstart dw 0x0 .rootsectors dw 0x0 .datastart dw 0x0 .file times 0xb db 0x20 .size dw 0x0 .cluster dw 0x0 .pointer dw 0x0 .driverrormsg db "Drive not found", 0x0 .diskerrormsg db "Unable to read disk", 0x0 .filerrormsg db "File or command not found", 0x0 .sizerrormsg db "Not enough memory", 0x0 ;*** ;Check the file name and convert to upper case .checkconv: ;Check for the string end, length limit, and invalid characters ;Check for the string end cmp al, 0x0 je .load ;Check for the length limit cmp bl, 0x0 je .error ;Check for invalid characters cmp al, 0x22 je .error cmp al, 0x2a jl .contcheck1 cmp al, 0x2c jg .contcheck1 jmp .error .contcheck1: cmp al, 0x2f je .error cmp al, 0x3a jl .contcheck2 cmp al, 0x3f jg .contcheck2 jmp .error .contcheck2: cmp al, 0x5b jl .contcheck3 cmp al, 0x5d jg .contcheck3 jmp .error .contcheck3: cmp al, 0x7c je .error ;Check for lower case and convert it to upper case ;Check for lower case cmp al, 0x61 jl .storech cmp al, 0x7a jg .storech ;Convert lower to upper case sub al, 0x20 ;Store the character .storech: stosb ;Decrease the counter dec bl ;Return ret ;*** ;Calculate the source arguments for loading data from the disk .calcsource: ;Store AX and BX in the stack push ax push bx ;Calculate the cylinder, head, and sector ;Store the logical sector in BX mov bx, ax ;Calculate the sector mov dx, 0x0 div word [.sectorspertrack] add dl, 0x1 mov cl, dl ;Load the logical sector from BX mov ax, bx ;Calculate the head and cylinder mov dx, 0x0 div word [.sectorspertrack] mov dx, 0x0 div word [.heads] mov dh, dl ;Head mov ch, al ;Cylinder ;Load the drive number mov dl, byte [.drive] ;Load BX and AX from the stack pop bx pop ax ;Return ret