;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 the initial registers 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 .start ;Check which drive to change to cmp byte [si], "a" je .cha cmp byte [si], "A" je .cha cmp byte [si], "b" je .chb cmp byte [si], "B" je .chb cmp byte [si], "c" je .chc cmp byte [si], "C" je .chc cmp byte [si], "d" je .chd cmp byte [si], "D" je .chd ;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 .cha: mov dl, 0x0 mov byte [.drive], dl jmp .drivechanged .chb: mov dl, 0x1 mov byte [.drive], dl jmp .drivechanged .chc: mov dl, 0x2 mov byte [.drive], dl jmp .drivechanged .chd: mov dl, 0x3 mov byte [.drive], dl .drivechanged: add si, 0x2 .start: ;Set DI at .file and initialise it with spaces mov di, .file mov cx, 0xb mov al, 0x20 rep stosb sub di, 0xb ;Convert .file into FAT formatting ;Initialise the length counter for the main part of the file name mov bl, 0x8 ;Convert the main part of the file name .nameloop: ;Load a character lodsb ;Check for a period cmp al, 0x2e je .initext ;Check for everything else and convert to upper case call .checkconv jmp .nameloop ;Convert the extension .initext: ;Set DI and initialise the length counter for the extension mov bl, 0x3 mov di, .file+0x8 .extloop: ;Load a character 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 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 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, word [buffer + 0xb] mov word [.sectorsize], ax mov al, byte [buffer + 0xd] mov byte [.clustersize], al mov ax, word [buffer + 0xe] mov word [.bootsectors], ax mov al, byte [buffer + 0x10] mov byte [.fats], al mov ax, word [buffer + 0x11] mov word [.rootentries], ax mov ax, word [buffer + 0x16] mov word [.sectorsperfat], ax mov ax, word [buffer + 0x18] mov word [.sectorspertrack], ax mov ax, word [buffer + 0x1a] mov word [.sides], ax ;Load the root ;Set the source mov ah, 0x0 mov al, [.fats] mul word [.sectorsperfat] add ax, [.bootsectors] push ax ;Set the destination mov si, buffer mov bx, si ;Set the size mov ax, [.rootentries] mov dx, 0x20 mul dx mov dx, 0x0 div word [.sectorsize] mov cx, ax pop ax push ax push cx .loadroot: push cx call .calcsource ;Set the size push ax mov al, 0x1 ;Load mov ah, 0x2 int 0x13 pop ax ;Set the next sector add ax, 0x1 add bx, word [.sectorsize] pop cx loop .loadroot ;Search the root for the file entry ;Set DI to the root mov di, buffer ;Initialise the search loop mov cx, word [.rootentries] mov ax, 0x0 .search: ;Store CX in the stack push cx ;Check for the file entry mov si, .file mov cx, 0xb rep cmpsb je .checksize ;Set DI to the next entry add ax, 0x20 mov di, buffer add di, ax ;Load CX from the stack pop cx 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 .clearstack ;Check and store the file size .checksize: ;Load CX from the stack 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 .clearstack ;Load the FAT .loadfat: ;Store the first cluster of the file mov ax, word [di + 0xf] mov word [.cluster], ax ;Set the source mov ax, 0x1 call .calcsource ;Set the destination mov di, buffer mov bx, di ;Set the size mov ax, [.sectorsperfat] ;Load mov ah, 0x2 int 0x13 ;Load the file ;Load a cluster .loadcluster: ;Set the source pop cx pop bx mov ax, word [.cluster] sub ax, 0x2 mul byte [.clustersize] add ax, bx add ax, cx push bx push cx ;Set the destination mov bx, word [.pointer] ;Load a sector mov ch, 0x0 mov cl, byte [.clustersize] .loadsector: push cx call .calcsource ;Set the size push ax mov al, 0x1 ;Load mov ah, 0x2 int 0x13 pop ax ;Set the next sector add ax, 0x1 add bx, word [.sectorsize] pop cx loop .loadsector ;Calculate the next cluster 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 [ds:si] or dx, dx jz .even shr ax, 1 shr ax, 1 shr ax, 1 shr ax, 1 jmp .contcalc .even: and ax, 0xfff .contcalc: mov word [.cluster], ax cmp ax, 0xff8 jge .success mov ax, [.sectorsize] mul word [.clustersize] add word [.pointer], ax 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 .clearstack: pop bx pop bx .done: ;Load the initial registers from the stack pop di pop si pop dx pop bx ;Set AH to its initial value mov ah, 0x0 iret ;Data .drive db 0x0 .sectorsize dw 0x0 ;bytes .clustersize db 0x0 ;sectors .bootsectors dw 0x0 .fats db 0x0 .rootentries dw 0x0 .sectorsperfat dw 0x0 .sectorspertrack dw 0x0 .sides 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 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 cmp al, 0x61 jl .storech cmp al, 0x7a jg .storech ;Convert lower to upper case sub al, 0x20 .storech: ;Store the character stosb ;Decrease the counter dec bl ret ;Calculate the source arguments for loading data from the disk .calcsource: push ax push bx mov bx, ax mov dx, 0x0 div word [.sectorspertrack] add dl, 0x1 mov cl, dl mov ax, bx mov dx, 0x0 div word [.sectorspertrack] mov dx, 0x0 div word [.sides] mov dh, dl mov ch, al pop bx pop ax mov dl, byte [.drive] ret