I am a beginner OS developer. Recently I wrote a simple, 16-bit operating system. I added FAT12 as a file system.
So what I did was:
- FAT12 bootloader (same as MikeOS bootloader)
- DIR command in the system kernel
And now I need to implement functionality for reading and displaying text files. But I don't have the necessary knowledge to implement this kind of thing, so I decided to ask here, because I'm at a dead end.
Please help me
Kernel source code:
Code: Select all
; ==================================================================
; x16-PRos -- The x16-PRos Operating System kernel
; Copyright (C) 2025 PRoX2011
;
; This is loaded from the second disk sector by BOOT.BIN
; ==================================================================
[BITS 16]
[ORG 0x0000]
start:
mov ax, 0x2000
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0xFFFE
cli
call set_video_mode
; Set up frequency (1193180 Hz / 1193 = ~1000 Hz)
mov al, 0xB6
out 0x43, al
mov ax, 1193
out 0x42, al
mov al, ah
out 0x42, al
call print_interface ; Help menu and headler
mov si, start_melody
call play_melody ; Startup melody
call shell ; PRos terminal
jmp $
set_video_mode:
; VGA 640*460, 16 colors
mov ax, 0x12
int 0x10
ret
print_string:
mov ah, 0x0E
mov bl, 0x0F
.print_char:
lodsb
cmp al, 0
je .done
int 0x10
jmp .print_char
.done:
ret
print_newline:
mov ah, 0x0E
mov al, 0x0D
int 0x10
mov al, 0x0A
int 0x10
ret
; ===================== Colored prints =====================
; ------ Green ------
print_string_green:
mov ah, 0x0E
mov bl, 0x0A
.print_char:
lodsb
cmp al, 0
je .done
int 0x10
jmp .print_char
.done:
ret
; ------ Cyan ------
print_string_cyan:
mov ah, 0x0E
mov bl, 0x0B
.print_char:
lodsb
cmp al, 0
je .done
int 0x10
jmp .print_char
.done:
ret
; ------ Red ------
print_string_red:
mov ah, 0x0E
mov bl, 0x0C
.print_char:
lodsb
cmp al, 0
je .done
int 0x10
jmp .print_char
.done:
ret
print_interface:
mov si, header
call print_string
call print_newline
mov si, menu
call print_string_green
call print_newline
ret
print_help:
mov si, menu
call print_string_green
call print_newline
ret
shell:
mov si, prompt
call print_string
call read_command
call print_newline
call execute_command
jmp shell
read_command:
mov di, command_buffer
xor cx, cx
.read_loop:
mov ah, 0x00
int 0x16
cmp al, 0x0D
je .done_read
cmp al, 0x08
je .handle_backspace
cmp cx, 255
jge .done_read
stosb
mov ah, 0x0E
mov bl, 0x1F
int 0x10
inc cx
jmp .read_loop
.handle_backspace:
cmp di, command_buffer
je .read_loop
dec di
dec cx
mov ah, 0x0E
mov al, 0x08
int 0x10
mov al, ' '
int 0x10
mov al, 0x08
int 0x10
jmp .read_loop
.done_read:
mov byte [di], 0
ret
execute_command:
mov si, command_buffer
; Checking the command "help"
mov di, help_str
call compare_strings
je do_help
mov si, command_buffer
; Checking the command "info"
mov di, info_str
call compare_strings
je print_OS_info
mov si, command_buffer
; Checking the command "cls"
mov di, cls_str
call compare_strings
je do_cls
mov si, command_buffer
; Checking the command "CPU"
mov di, CPU_str
call compare_strings
je do_CPUinfo
mov si, command_buffer
; Checking the command "date"
mov di, date_str
call compare_strings
je print_date
mov si, command_buffer
; Checking the command "time"
mov di, time_str
call compare_strings
je print_time
mov si, command_buffer
; Checking the command "shut"
mov di, shut_str
call compare_strings
je do_shutdown
mov si, command_buffer
; Checking the command "reboot"
mov di, reboot_str
call compare_strings
je do_reboot
mov si, command_buffer
; Checking the command "dir"
mov di, dir_str
call compare_strings
je list_directory
call unknown_command
ret
compare_strings:
xor cx, cx
.next_char:
lodsb
cmp al, [di]
jne .not_equal
cmp al, 0
je .equal
inc di
jmp .next_char
.not_equal:
ret
.equal:
ret
do_help:
call print_newline
call print_help
call print_newline
ret
do_cls:
pusha
mov ax, 0x12
int 0x10
popa
ret
unknown_command:
mov si, unknown_msg
call print_string_red
call print_newline
ret
do_shutdown:
mov si, shut_melody
call play_melody
mov ax, 0x5307
mov bx, 0x0001
mov cx, 0x0003
int 0x15
ret
do_reboot:
int 0x19
ret
print_OS_info:
mov si, info
call print_string_green
call print_newline
ret
; ===================== CPU info functions =====================
print_edx:
mov ah, 0eh
; mov bh, 0 (Use it for very old BIOS)
mov bx, 4
.loop4r:
mov al, dl
int 10h
ror edx, 8
dec bx
jnz .loop4r
ret
print_full_name_part:
cpuid
push edx
push ecx
push ebx
push eax
mov cx, 4
.loop4n:
pop edx
call print_edx
loop .loop4n
ret
; ------ Print CPU cores number ------
print_cores:
mov si, cores
call print_string
mov eax, 1
cpuid
ror ebx, 16
mov al, bl
call print_al
ret
; ------ Print CPU cache line ------
print_cache_line:
mov si, cache_line
call print_string
mov eax, 1
cpuid
ror ebx, 8
mov al, bl
mov bl, 8
mul bl
call print_al
ret
; ------ Print CPU stepping ID ------
print_stepping:
mov si, stepping
call print_string
mov eax, 1
cpuid
and al, 15
call print_al
ret
print_al:
mov ah, 0
mov dl, 10
div dl
add ax, '00'
mov dx, ax
mov ah, 0eh
mov al, dl
cmp dl, '0'
jz skip_fn
mov bl, 0x0F
int 10h
skip_fn:
mov al, dh
mov bl, 0x0F
int 10h
ret
; ------- Print all CPU information ------
do_CPUinfo:
pusha
mov si, cpu_name
call print_string
; Displaying information about the CPU
mov eax, 80000002h
call print_full_name_part
mov eax, 80000003h
call print_full_name_part
mov eax, 80000004h
call print_full_name_part
mov si, mt
call print_string
call print_cores
mov si, mt
call print_string
call print_cache_line
mov si, mt
call print_string
call print_stepping
mov si, mt
call print_string
popa
ret
; ===================== Date and time functions =====================
; Function for displaying date
; Displays date in DD.MM.YY format
print_date:
mov si, date_msg
call print_string
pusha
; Get the date: ch - century, cl - year, dh - month, dl - day
mov ah, 0x04
int 0x1a
mov ah, 0x0e
; Print day (dl)
mov al, dl
shr al, 4
add al, '0'
mov bl, 0x0B
int 0x10
mov al, dl
and al, 0x0F
add al, '0'
int 0x10
; Print dot
mov al, '.'
mov bl, 0x0B
int 0x10
; Print mounth (dh)
mov al, dh
shr al, 4
add al, '0'
mov bl, 0x0B
int 0x10
mov al, dh
and al, 0x0F
add al, '0'
mov bl, 0x0B
int 0x10
; Print dot
mov al, '.'
mov bl, 0x0B
int 0x10
; Print year (cl)
mov al, cl
shr al, 4
add al, '0'
mov bl, 0x0B
int 0x10
mov al, cl
and al, 0x0F
add al, '0'
mov bl, 0x0B
int 0x10
mov si, mt
call print_string
popa
ret
; Function for displaying time
; Displays date in HH.MM.SS format
print_time:
mov si, time_msg
call print_string
pusha
; Get time: ch - hours, cl - minutes, dh - seconds
mov ah, 0x02
int 0x1a
mov ah, 0x0e
; Print hours (ch)
mov al, ch
shr al, 4
add al, '0'
mov bl, 0x0B
int 0x10
mov al, ch
and al, 0x0F
add al, '0'
mov bl, 0x0B
int 0x10
; Print separator
mov al, ':'
mov bl, 0x0B
int 0x10
; Print minutes (cl)
mov al, cl
shr al, 4
add al, '0'
mov bl, 0x0B
int 0x10
mov al, cl
and al, 0x0F
add al, '0'
mov bl, 0x0B
int 0x10
; Print separator
mov al, ':'
mov bl, 0x0B
int 0x10
; Print seconds (dh)
mov al, dh
shr al, 4
add al, '0'
mov bl, 0x0B
int 0x10
mov al, dh
and al, 0x0F
add al, '0'
mov bl, 0x0B
int 0x10
mov si, mt
call print_string
popa
ret
; ===================== PC speaker functions =====================
; ------ Turn on the speaker ------
on_pc_speaker:
pusha
in al, 0x61
or al, 0x03
out 0x61, al
popa
ret
; ------ Turn off the speaker ------
off_pc_speaker:
pusha
in al, 0x61
and al, 0xFC
out 0x61, al
popa
ret
; ------ Startup sound ------
play_melody:
pusha
; mov si, melody
.next_note:
mov ax, [si]
cmp ax, 0
je .done
mov dx, [si+2]
add si, 4
call set_frequency
call on_pc_speaker
call delay_ms
call off_pc_speaker
jmp .next_note
.done:
popa
ret
set_frequency:
push ax
mov al, 0xB6
out 0x43, al
pop ax
out 0x42, al
mov al, ah
out 0x42, al
ret
delay_ms:
pusha
mov ax, dx
mov cx, 1000
mul cx
mov cx, dx
mov dx, ax
mov ah, 0x86
int 0x15
popa
ret
; ========== DISK FUNCTIONS ==========
disk_reset_floppy:
push ax
push dx
mov ah, 0
mov dl, 0
stc
int 13h
pop dx
pop ax
ret
disk_convert_l2hts:
push bx
push ax
mov bx, ax
mov dx, 0
div word [SecsPerTrack]
add dl, 1
mov cl, dl
mov ax, bx
mov dx, 0
div word [SecsPerTrack]
mov dx, 0
div word [Sides]
mov dh, dl
mov ch, al
pop ax
pop bx
mov dl, 0
ret
; ------ Getting FAT12 files list ------
get_file_list:
pusha
mov word [.file_list_tmp], ax
mov eax, 0
call disk_reset_floppy
mov ax, 19
call disk_convert_l2hts
mov si, disk_buffer
mov bx, si
mov ah, 2
mov al, 14
pusha
.read_root_dir:
popa
pusha
stc
int 13h
call disk_reset_floppy
jnc .show_dir_init
call disk_reset_floppy
jnc .read_root_dir
jmp .done
.show_dir_init:
popa
mov ax, 0
mov si, disk_buffer
mov word di, [.file_list_tmp]
.start_entry:
mov al, [si+11]
cmp al, 0Fh
je .skip
test al, 18h
jnz .skip
mov al, [si]
cmp al, 229
je .skip
cmp al, 0
je .done
mov cx, 1
mov dx, si
.testdirentry:
inc si
mov al, [si]
cmp al, ' '
jl .nxtdirentry
cmp al, '~'
ja .nxtdirentry
inc cx
cmp cx, 11
je .gotfilename
jmp .testdirentry
.gotfilename:
mov si, dx
mov cx, 0
.loopy:
mov byte al, [si]
cmp al, ' '
je .ignore_space
mov byte [di], al
inc si
inc di
inc cx
cmp cx, 8
je .add_dot
cmp cx, 11
je .done_copy
jmp .loopy
.ignore_space:
inc si
inc cx
cmp cx, 8
je .add_dot
jmp .loopy
.add_dot:
mov byte [di], '.'
inc di
jmp .loopy
.done_copy:
mov byte [di], ','
inc di
.nxtdirentry:
mov si, dx
.skip:
add si, 32
jmp .start_entry
.done:
dec di
mov byte [di], 0
popa
ret
.file_list_tmp dw 0
; ------ Print list of files ------
list_directory:
call print_newline
mov cx, 0
mov ax, dirlist
call get_file_list
mov si, dirlist
mov ah, 0Eh
.repeat:
lodsb
cmp al, 0
je .done
cmp al, ','
jne .nonewline
pusha
call print_newline
popa
jmp .repeat
.nonewline:
int 10h
jmp .repeat
.done:
call print_newline
call print_newline
jmp shell
; ===================== Data section =====================
; ------ Headler ------
header db '============================= x16 PRos v0.4 ====================================', 0
; ------ Help menu ------
menu db 0xC9, 47 dup(0xCD), 0xBB, 10, 13 ; ╔═══════════════════════════════════════════════╗
db 0xBA, 'Commands: ', 0xBA, 10, 13 ; ║ ... ║
db 0xBA, ' help - get list of the commands ', 0xBA, 10, 13
db 0xBA, ' info - print information about OS ', 0xBA, 10, 13
db 0xBA, ' cls - clear terminal ', 0xBA, 10, 13
db 0xBA, ' shut - shutdown PC ', 0xBA, 10, 13
db 0xBA, ' reboot - restart system ', 0xBA, 10, 13
db 0xBA, ' date - print current date (DD.MM.YY) ', 0xBA, 10, 13
db 0xBA, ' time - print current time (HH.MM.SS) ', 0xBA, 10, 13
db 0xBA, ' CPU - print CPU info ', 0xBA, 10, 13
db 0xBA, ' dir - list files on disk ', 0xBA, 10, 13
db 0xC0, 47 dup(0xCD), 0xBC, 10, 13, 0 ; ╚═══════════════════════════════════════════════╝
; ------ OS info text ------
info db 10, 13
db 0xC9, 46 dup(0xCD), 0xBB, 10, 13 ; ╔══════════════════════════════════════════════╗
db 0xBA, ' x16 PRos is the simple 16 bit operating ', 0xBA, 10, 13 ; ║ ... ║
db 0xBA, ' system written in NASM for x86 PC`s ', 0xBA, 10, 13
db 0xC3, 46 dup(0xC4), 0xB4, 10, 13 ; ╠══════════════════════════════════════════════╣
db 0xBA, ' Autor: PRoX ', 0xBA, 10, 13
db 0xBA, ' Video mode: 0x12 (640x480; 16 colors) ', 0xBA, 10, 13
db 0xBA, ' File system: FAT12 ', 0xBA, 10, 13
db 0xBA, ' License: MIT ', 0xBA, 10, 13
db 0xBA, ' Version: 0.4 ', 0xBA, 10, 13
db 0xC0, 46 dup(0xCD), 0xBC, 10, 13, 0 ; ╚══════════════════════════════════════════════╝
; ------ Commands ------
help_str db 'help', 0
info_str db 'info', 0
cls_str db 'cls', 0
shut_str db 'shut', 0
reboot_str db 'reboot', 0
CPU_str db 'CPU', 0
date_str db 'date', 0
time_str db 'time', 0
dir_str db 'dir', 0
unknown_msg db 'Unknown command.', 0 ; Uncnow command message
prompt db '[PRos] > ', 0 ; Terminal prompt
mt db '', 10, 13, 0 ; \n
command_buffer db 128 dup(0) ; Buffer for entered command
dirlist times 1024 db 0 ; Buffer for directory list
; ------ CPU ------
cpu_name db ' CPU name: ', 0
cores db ' CPU cores: ', 0
stepping db ' Stepping ID: ', 0
cache_line db ' Cache line: ', 0
time_msg db 'Current time: ', 0
date_msg db 'Current date: ', 0
; ------ Sounds ------
start_melody:
dw 1811, 250 ; E5
dw 1015, 250 ; D6
dw 761, 250 ; G6
dw 0, 0 ; Melody end
shut_melody:
dw 761, 250 ; G6
dw 1015, 250 ; D6
dw 1811, 250 ; E5
dw 0, 0 ; Melody end
error_sound:
dw 2415, 250 ; B4
dw 2415, 250 ; B4
dw 0, 0 ; Melody end
disk_buffer times 8192 db 0 ; Disk buffer (8 KB)
Sides dw 2 ; Number of floppy sides (heads)
SecsPerTrack dw 18 ; Sectors per track