diff --git a/data/README.md b/data/README.md new file mode 100755 index 0000000..14b687a --- /dev/null +++ b/data/README.md @@ -0,0 +1,9 @@ +Kernel written in Assmebly +I'm using a modified JazzOS Bootloader + +Commands: +CAT +HELP +CLEAR +REBOOT +PONG diff --git a/data/var.cws b/data/var.cws new file mode 100644 index 0000000..fed79d0 --- /dev/null +++ b/data/var.cws @@ -0,0 +1 @@ +x = 5 diff --git a/source/kernel/data.asm b/source/kernel/data.asm new file mode 100644 index 0000000..93b3422 --- /dev/null +++ b/source/kernel/data.asm @@ -0,0 +1,58 @@ +; Define Fat12 header +bdb_oem: db 'MSWIN4.1' ; ignore +bdb_bytes_per_sector: dw 200h ; = 512d +bdb_sectors_per_cluster: db 01h ; sector = cluster +bdb_reserved_sectors: dw 01h +bdb_fat_count: db 02h ; We've got a fat1 and fat2 +bdb_dir_entries_count: dw 0E0h ; Maximum number of root directory entries +bdb_total_sectors: dw 0B40h ; = 2880d +bdb_media_descriptor_type: db 0F0h ; ignore +bdb_sectors_per_fat: dw 09h +bdb_sectors_per_track: dw 12h ; = 18d +bdb_number_of_heads: dw 02h ; top and bottom of the floppy disk +bdb_hidden_sectors: dd 0 ; ignore +bdb_large_sector_count: dd 0 ; total sector count for fat32 (0 for fat12 or fat16) + +; extended boot record +ebr_drive_number: db 0 ; ignore + db 0 ; ignore +ebr_signature: db 29h ; boot signature, indicates that the next three fields are present (0x29) +ebr_volume_id: db 12h, 34h, 56h, 78h ; unique id for volume tracking +ebr_volume_label: db 'CRAWOS0.0.6' ; must be 11 bytes +ebr_system_id: db 'FAT12 ' ; must be 8 bytes + + +fat12_file_name: db ' ' ; 11 free bytes to store a filename in +boot_message: db 'OK] Kernel successfully loaded!\n\n', 0 +user_input: times 20 db 0 +prompt_length: db 20 +prompt: db 'sh > ', 0 +help_string: db 'HELP', 0 +clear_string: db 'CLEAR', 0 +reboot_string: db 'REBOOT', 0 +pong_string: db 'PONG', 0 +cat_string: db 'CAT', 0 +ls_string: db 'LS', 0 +help_text: db 'This is for Cowards:\n"LS" to list the directory,\n"CAT" to output the contents of a file,\n"HELP" for this helpful text,\n"CLEAR" to clear the screen,\n"REBOOT" or esc to reboot\n', 0 +command_result_text: db 'You typed: ', 0 +unknown_command: db 'Error: Unkown Command..\n ', 0 + +; Disk operations +disk_read_fail: db 'Error: Could not read disk\n', 0 +file_not_found: db 'File not found\n', 0 +too_long_filename: db 'Filename too long for Fat12\n', 0 +file_found: db 'File found\n', 0 +loading_root: db 'Loading root diretory\n', 0 +read_only: db 'Read Only', 0 ; 1 +hidden: db ' Hidden ', 0 ; 2 +system: db ' System ', 0 ; 4 +volume: db ' Volume ', 0 ; 8 +directory: db 'Directory', 0 ; 10 +archive: db ' Archive ', 0 ; 20 +file_name_length: db 0 +file_cluster: dw 0 +file_length: dd 0 +read_write_flag: db 02 +empty_byte: db 0 +empty_word: dw 0 +empty_dword: dd 0 diff --git a/source/kernel/features/cli.asm b/source/kernel/features/cli.asm index 07ef453..9b02459 100755 --- a/source/kernel/features/cli.asm +++ b/source/kernel/features/cli.asm @@ -44,6 +44,20 @@ os_read_cli: call os_compare_strings cmp cl, 1 je power_reboot + + ; Cat + mov si, user_input + mov di, cat_string + call os_compare_strings + cmp cl, 1 + je cat + + ; LS + mov si, user_input + mov di, ls_string + call os_compare_strings + cmp cl, 1 + je ls ; Pong mov si, user_input @@ -78,16 +92,9 @@ pong: call game_pong call os_read_cli.finish -section .data - welcome_text db 'Welcome to CrawOS, the Cwick, Real and AWsome Operating System\n', 0 - user_input times 20 db 0 - prompt_length db 20 - prompt db 'CrawOS sh> ', 0 - help_string db 'HELP', 0 - clear_string db 'CLEAR', 0 - reboot_string db 'REBOOT', 0 - cat_string db 'CAT', 0 - pong_string db 'PONG', 0 - help_text db 'This is for Cowards:\n"HELP" for this helpful text,\n"CLEAR" to clear the screen,\n"REBOOT" or esc to reboot\n', 0 - command_result_text db 'You typed: ', 0 - unknown_command db 'Error: Unkown Command.. \n', 0 +cat: + call util_cat + call os_read_cli.finish +ls: + call util_ls + call os_read_cli.finish diff --git a/source/kernel/features/disk.asm b/source/kernel/features/disk.asm index 60cade1..e9a4327 100644 --- a/source/kernel/features/disk.asm +++ b/source/kernel/features/disk.asm @@ -14,7 +14,7 @@ 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 + 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 @@ -53,28 +53,34 @@ disk_load_root: ; --------------------------------------------- ; Reads a certain number of sectors into memory -; int 13h/ah = 02h read disk 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: +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, 02h ; BIOS ah code for read disk sectors to memory + mov ah, [read_write_flag] + mov al, 1 ; repeat drive read 3 times (incase of random error) mov di, 3 ; counter @@ -122,10 +128,55 @@ disk_read: 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 @@ -134,7 +185,7 @@ disk_load_file: xor bx,bx .search_root: mov di, fat12_file_name ; Move the name of the kernel into di - mov cx, 11 ; length of 'KERNEL BIN' + 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 @@ -147,29 +198,179 @@ disk_load_file: 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, 20h + add ax, 1Fh mov [file_cluster], ax ; Setup registers for disk read - mov bx, data_buffer + mov si, file_buffer + mov bx, si mov dl, [ebr_drive_number] - call disk_read ; Load file from disk into memory + 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 -section .data: - disk_read_fail: db 'Error: Could not read disk\n', 0 - file_not_found: db 'File not found\n', 0 - file_found: db 'File found\n', 0 - loading_root: db 'Loading root diretory\n', 0 - file_cluster: dw 0 - fat12_file_name: db ' ' + 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 diff --git a/source/kernel/features/strings.asm b/source/kernel/features/strings.asm index 31f49eb..4f16f7d 100755 --- a/source/kernel/features/strings.asm +++ b/source/kernel/features/strings.asm @@ -62,8 +62,7 @@ os_format_fat_filename: pusha call os_upper_case call os_string_length ; Stores the length of the string in cl - mov bl, 11 - sub bl, cl ; 11 - string_length + xor ch,ch ; Clear ch to reset it to 0 .character_loop: lodsb cmp al, 0 @@ -71,10 +70,18 @@ os_format_fat_filename: cmp al, 2Eh ; 2Eh je .add_spaces ; This will end up back at .character_loop stosb + inc ch jmp .character_loop .add_spaces: ; Add the number of spaces as bl holds mov al, ' ' ; 20h = space + + ; Work out the number of spaces in between + ; the name and extension. + ; 8 - name_length(ch) + xor bl, bl + sub bl, ch + add bl, 7 .spaces_loop: stosb cmp bl, 0 @@ -85,6 +92,58 @@ os_format_fat_filename: popa ret +; Does the inverse of the previous +; Converts a fat filename back to human readable +; eg. 'KERNEL BIN' -> 'KERNEL.BIN' +; input: si points to fat filename (11 bytes) +; output: +; di points to the unformatted filename +; [file_name_length] stores the length of the filename +string_unformat_fat_filename: + pusha + + xor ax,ax + xor dx,dx + mov cx, 11 ; Counter + .name_loop: + lodsb + stosb + dec cx + inc dx + cmp cx, 3 + jne .name_loop + push si + mov si, di + .space_loop: + dec si + lodsb + dec si + dec dx + cmp al, 20h ; Space + je .space_loop + jmp .insert_stop + .insert_stop: + mov di, si + inc di + inc dx + pop si + mov al, 2Eh + stosb + mov cx, 3 + .extension_loop: + lodsb + stosb + dec cx + inc dx + cmp cx, 0 + jne .extension_loop + + .finish: + inc dx + mov [file_name_length], dx + popa + ret + ; Convert a string to all upper/lower case ; INPUT: si pointing to a string ; OUPUT: the same string in memory will now be capitalised/decapitalised diff --git a/source/kernel/features/text.asm b/source/kernel/features/text.asm index 500fb01..f2fdc1f 100755 --- a/source/kernel/features/text.asm +++ b/source/kernel/features/text.asm @@ -11,68 +11,96 @@ os_print_string: lodsb ; Get char from si into al cmp al, 0 ; Compare al to 0 je .done ; If char is zero, end of string + + cmp al, 0Ah ; When there's an NL or CR, do both, linux encodes files only with NL + je .new_line + cmp al, 0Dh + je .repeat + cmp al, 5Ch ; backslash je .backslash int 10h ; Otherwise, print it jmp .repeat ; And move on to next char - .backslash: ; If there is a '\', do what it says, \n for newline, \t for tab etc - lodsb - dec si - cmp al, 6Eh ; 'n' - je .newline - cmp al, 74h ; \t - je .tab - cmp al, 5Ch ; '\' - je .another_backslash - jmp .repeat - .newline: - mov al, 0Ah ; new line - int 10h - mov al, 0Dh ; carriage return - int 10h - jmp .finish_backslash - .tab: - mov al, 09h ; tab - int 10h - jmp .finish_backslash - .another_backslash: ; This just prints 1 backslash - mov al, 5Ch - int 10h - jmp .finish_backslash - .finish_backslash: - inc si + .new_line: + mov al, 0Ah + int 10h + mov al, 0Dh + int 10h jmp .repeat + .backslash: + call text_backslash + jmp .repeat .done: popa ret ; This is similar to the previous, however it prints ; raw output (including null) and prints the number -; of character defined by ax +; of character defined by cx ; IN: ; SI = pointer to start of string to be printed -; AX = Length of string to print -text_raw_output: +; CX = Length of string to print +text_print_raw: pusha + add cx, 1 + mov ah, 0Eh + .repeat: + dec cx + cmp cx, 0 + je .finish + lodsb + cmp al, 00h ; Print a space in place of a Null + je .space + cmp al, 0Ah ; When there's an NL or CR, do both, linux encodes files only with NL + je .new_line + cmp al, 0Dh + je .repeat + int 10h + jmp .repeat + .space: + mov al, 20h + int 10h + jmp .repeat + .new_line: + mov al, 0Ah + int 10h + mov al, 0Dh + int 10h + jmp .repeat + .finish: + popa + ret - mov di, ax - - mov ah, 0Eh ; int 10h teletype function, we're telling the BIOS we will print something - -.repeat: - lodsb ; Get char from si into al - int 10h ; Otherwise, print it - dec di - cmp di, 00h - je .done - jne .repeat -.done: - popa - ret - - +; NO PUSHING/POPPING! +text_backslash: ; If there is a '\', do what it says, \n for newline, \t for tab etc + lodsb + dec si + cmp al, 6Eh ; 'n' + je .newline + cmp al, 74h ; \t + je .tab + cmp al, 5Ch ; '\' + je .another_backslash + ret + .newline: + mov al, 0Ah ; new line + int 10h + mov al, 0Dh ; carriage return + int 10h + jmp .finish_backslash + .tab: + mov al, 09h ; tab + int 10h + jmp .finish_backslash + .another_backslash: ; This just prints 1 backslash + mov al, 5Ch + int 10h + jmp .finish_backslash + .finish_backslash: + inc si + ret ; -------------------------------------------- os_print_newline: diff --git a/source/kernel/features/time.asm b/source/kernel/features/time.asm new file mode 100644 index 0000000..ff107e4 --- /dev/null +++ b/source/kernel/features/time.asm @@ -0,0 +1,8 @@ +time_get_time: + pusha + + mov ah, 00h + int 1Ah + + popa + ret diff --git a/source/kernel/features/utils.asm b/source/kernel/features/utils.asm new file mode 100644 index 0000000..7bdd263 --- /dev/null +++ b/source/kernel/features/utils.asm @@ -0,0 +1,28 @@ +util_cat: + pusha + + call disk_clear_file_buffer + mov si, user_input + ; TODO make this more consistent to account for double spaces + add si, 4 ; Move si to after 'CAT' + + call disk_load_file + mov si, file_buffer + mov cx, [file_length] + call text_print_raw + + popa + ret + +util_ls: + pusha + + call disk_clear_output_buffer + call disk_list_contents + + mov si, output_buffer + call os_print_string + + popa + ret + diff --git a/source/kernel/kernel.asm b/source/kernel/kernel.asm index ab044c5..6700bd0 100644 --- a/source/kernel/kernel.asm +++ b/source/kernel/kernel.asm @@ -1,17 +1,17 @@ ORG 00h BITS 16 -root_buffer equ 24000h -data_buffer equ 26000h +root_buffer: equ 24000h ; Stores a dump of the root directory +metadata_buffer: equ 27000h ; Stores metadata about whatever's in the data buffer +output_buffer: equ 27020h ; Buffer storing stuff you'll output +file_buffer: equ 28000h ; Stores actual data to be read start: + call disk_load_root ; Loads the root directory into disk_buffer for future use + mov si, boot_message call os_print_string - - call disk_load_root ; Loads the root directory into disk_buffer - - ; Physical address = (segment * 16) + offset - mov si, file_name - call disk_load_file + mov si, help_text + call os_print_string call os_start_cli hlt @@ -19,31 +19,6 @@ start: halt: jmp halt -boot_message: db 'OK] Kernel successfully loaded!\n\n', 0 -file_name: db 'hello.cws', 0 - -; Define Fat12 header -bdb_oem: db 'MSWIN4.1' ; ignore -bdb_bytes_per_sector: dw 200h ; = 512d -bdb_sectors_per_cluster: db 01h ; sector = cluster -bdb_reserved_sectors: dw 01h -bdb_fat_count: db 02h ; We've got a fat1 and fat2 -bdb_dir_entries_count: dw 0E0h ; Maximum number of root directory entries -bdb_total_sectors: dw 0B40h ; = 2880d -bdb_media_descriptor_type: db 0F0h ; ignore -bdb_sectors_per_fat: dw 09h -bdb_sectors_per_track: dw 12h ; = 18d -bdb_number_of_heads: dw 02h ; top and bottom of the floppy disk -bdb_hidden_sectors: dd 0 ; ignore -bdb_large_sector_count: dd 0 ; total sector count for fat32 (0 for fat12 or fat16) - -; extended boot record -ebr_drive_number: db 0 ; ignore - db 0 ; ignore -ebr_signature: db 29h ; boot signature, indicates that the next three fields are present (0x29) -ebr_volume_id: db 12h, 34h, 56h, 78h ; unique id for volume tracking -ebr_volume_label: db 'CrawShaw OS' ; must be 11 bytes -ebr_system_id: db 'FAT12 ' ; must be 8 bytes ; ------------------------------------------------------------------ ; FEATURES -- Code to pull into the kernel @@ -53,9 +28,14 @@ ebr_system_id: db 'FAT12 ' ; must be 8 bytes %INCLUDE "source/kernel/features/power.asm" %INCLUDE "source/kernel/features/strings.asm" %INCLUDE "source/kernel/features/graphics.asm" - %INCLUDE "source/kernel/features/disk.asm" - %INCLUDE "source/kernel/features/math.asm" + %INCLUDE "source/kernel/features/disk.asm" + %INCLUDE "source/kernel/features/math.asm" + %INCLUDE "source/kernel/features/time.asm" + %INCLUDE "source/kernel/features/utils.asm" ; GAMES -- Games that I wrote for it %INCLUDE "source/kernel/games/pong.asm" +; DATA/VARIABLES + %INCLUDE "source/kernel/data.asm" +