home | terminal | snapshot | hardware | microcode engine | operating system | c compiler & assembler | c library | resources | photos & videos | system overview | interrupts & dma | instruction set | system verilog model | kernel overview | file system | system calls | shell | programs & games | how to build a program | github | youtube | contact

The operating system is called Solarium and is Unix based. It is very much a work in progress and a prototype, but I plan to make it a fully features operating system.

Broadly speaking it is split into a kernel and a shell.

The source code is available here.
For a quick demonstration, here is the code for the current kernel:

; ------------------------------------------------------------------------------------------------------------------;
; Solarium - Sol-1 Homebrew Minicomputer Operating System Kernel.
; ------------------------------------------------------------------------------------------------------------------;
; Memory Map
; ------------------------------------------------------------------------------------------------------------------;
; 0000    ROM BEGIN
; ....
; 7FFF    ROM END
;
; 8000    RAM begin
; ....
; F7FF    Stack root
; ------------------------------------------------------------------------------------------------------------------;
; I/O MAP
; ------------------------------------------------------------------------------------------------------------------;
; FF80    UART 0    (16550)
; FF90    UART 1    (16550)
; FFA0    RTC       (M48T02)
; FFB0    PIO 0     (8255)
; FFC0    PIO 1     (8255)
; FFD0    IDE       (Compact Flash / PATA)
; FFE0    Timer     (8253)
; FFF0    BIOS CONFIGURATION NV-RAM STORE AREA
; ------------------------------------------------------------------------------------------------------------------;

; ------------------------------------------------------------------------------------------------------------------;
; System Constants
; ------------------------------------------------------------------------------------------------------------------;
_UART0_DATA       .equ $FF80            ; data
_UART0_DLAB_0     .equ $FF80            ; divisor latch low byte
_UART0_DLAB_1     .equ $FF81            ; divisor latch high byte
_UART0_IER        .equ $FF81            ; Interrupt enable register
_UART0_FCR        .equ $FF82            ; FIFO control register
_UART0_LCR        .equ $FF83            ; line control register
_UART0_LSR        .equ $FF85            ; line status register

_UART1_DATA       .equ $FF90            ; data
_UART1_DLAB_0     .equ $FF90            ; divisor latch low byte
_UART1_DLAB_1     .equ $FF91            ; divisor latch high byte
_UART1_IER        .equ $FF91            ; Interrupt enable register
_UART1_FCR        .equ $FF92            ; FIFO control register
_UART1_LCR        .equ $FF93            ; line control register
_UART1_LSR        .equ $FF95            ; line status register

XON               .equ $11
XOFF              .equ $13

_ide_BASE         .equ $FFD0            ; IDE BASE
_ide_R0           .equ _ide_BASE + 0    ; DATA PORT
_ide_R1           .equ _ide_BASE + 1    ; READ: ERROR CODE, WRITE: FEATURE
_ide_R2           .equ _ide_BASE + 2    ; NUMBER OF SECTORS TO TRANSFER
_ide_R3           .equ _ide_BASE + 3    ; SECTOR ADDRESS LBA 0 [0:7]
_ide_R4           .equ _ide_BASE + 4    ; SECTOR ADDRESS LBA 1 [8:15]
_ide_R5           .equ _ide_BASE + 5    ; SECTOR ADDRESS LBA 2 [16:23]
_ide_R6           .equ _ide_BASE + 6    ; SECTOR ADDRESS LBA 3 [24:27 (LSB)]
_ide_R7           .equ _ide_BASE + 7    ; READ: STATUS, WRITE: COMMAND

_7SEG_DISPLAY     .equ $FFB0            ; BIOS POST CODE HEX DISPLAY (2 DIGITS)
_BIOS_POST_CTRL   .equ $FFB3            ; BIOS POST DISPLAY CONTROL REGISTER, 80h = As Output
_PIO_A            .equ $FFB0    
_PIO_B            .equ $FFB1
_PIO_C            .equ $FFB2
_PIO_CONTROL      .equ $FFB3            ; PIO CONTROL PORT

_TIMER_C_0        .equ $FFE0            ; TIMER COUNTER 0
_TIMER_C_1        .equ $FFE1            ; TIMER COUNTER 1
_TIMER_C_2        .equ $FFE2            ; TIMER COUNTER 2
_TIMER_CTRL       .equ $FFE3            ; TIMER CONTROL REGISTER

STACK_BEGIN       .equ $F7FF            ; beginning of stack
FIFO_SIZE         .equ 1024

text_org          .equ $400
; ------------------------------------------------------------------------------------------------------------------;


; For the next iteration:
; boot-sector(1) | kernel-sectors(32) | inode-bitmap | rawdata-bitmap | inode-table | raw-disk-data
; inode-table format:
;  file-type(f, d)
;  permissons
;  link-count
;  filesize
;  time-stamps
;  15 data block pointers
;  single-indirect pointer

; FILE ENTRY ATTRIBUTES
; filename (24)
; attributes (1)       :|0|0|file_type(3bits)|x|w|r|
; LBA (2)              : location of raw data for file entry, or dirID for directory entry
; size (2)             : filesize
; day (1)           
; month (1)
; year (1)
; packet size = 32 bytes  : total packet size in bytes

FST_ENTRY_SIZE          .equ 32  ; bytes
FST_FILES_PER_SECT      .equ (512 / FST_ENTRY_SIZE)
FST_FILES_PER_DIR       .equ (512 / FST_ENTRY_SIZE)
FST_NBR_DIRECTORIES     .equ 64
                        ; 1 sector for header, the rest is for the list of files/dirs
FST_SECTORS_PER_DIR     .equ (1 + (FST_ENTRY_SIZE * FST_FILES_PER_DIR / 512))    
FST_TOTAL_SECTORS       .equ (FST_SECTORS_PER_DIR * FST_NBR_DIRECTORIES)
FST_LBA_START           .equ 32
FST_LBA_END             .equ (FST_LBA_START + FST_TOTAL_SECTORS - 1)

FS_NBR_FILES            .equ (FST_NBR_DIRECTORIES * FST_FILES_PER_DIR)
FS_SECTORS_PER_FILE     .equ 32         ; the first sector is always a header with a NULL parameter (first byte)
                                        ; so that we know which blocks are free or taken
FS_FILE_SIZE            .equ (FS_SECTORS_PER_FILE * 512)                  
FS_TOTAL_SECTORS        .equ (FS_NBR_FILES * FS_SECTORS_PER_FILE)
FS_LBA_START            .equ (FST_LBA_END + 1)
FS_LBA_END              .equ (FS_LBA_START + FS_NBR_FILES - 1)

root_id:                .equ FST_LBA_START

; ------------------------------------------------------------------------------------------------------------------;
; GLOBAL SYSTEM VARIABLES
; ------------------------------------------------------------------------------------------------------------------;

; ------------------------------------------------------------------------------------------------------------------;
; IRQ Table
; Highest priority at lowest address
; ------------------------------------------------------------------------------------------------------------------;
.dw int_0
.dw int_1
.dw int_2
.dw int_3
.dw int_4
.dw int_5
.dw int_6
.dw int_7

; ------------------------------------------------------------------------------------------------------------------;
; Reset Vector
; ------------------------------------------------------------------------------------------------------------------;
.dw kernel_reset_vector

; ------------------------------------------------------------------------------------------------------------------;
; Exception Vector Table
; Total of 7 entries, starting at address $0012
; ------------------------------------------------------------------------------------------------------------------;
.dw trap_privilege
.dw trap_div_zero
.dw trap_undef_opcode
.dw 0
.dw 0
.dw 0
.dw 0

; ------------------------------------------------------------------------------------------------------------------;
; System Call Vector Table
; Starts at address $0020
; ------------------------------------------------------------------------------------------------------------------;
.dw syscall_break
.dw syscall_rtc
.dw syscall_ide
.dw syscall_io
.dw syscall_file_system
.dw syscall_spawn_proc
.dw syscall_list_procs
.dw syscall_datetime
.dw syscall_reboot
.dw syscall_pause_proc
.dw syscall_resume_proc
.dw syscall_terminate_proc
.dw syscall_system

; ------------------------------------------------------------------------------------------------------------------;
; System Call Aliases
; ------------------------------------------------------------------------------------------------------------------;
sys_break            .equ 0
sys_rtc              .equ 1
sys_ide              .equ 2
sys_io               .equ 3
sys_filesystem       .equ 4
sys_spawn_proc       .equ 5
sys_list             .equ 6
sys_datetime         .equ 7
sys_reboot           .equ 8
sys_pause_proc       .equ 9
sys_resume_proc      .equ 10
sys_terminate_proc   .equ 11
sys_system           .equ 12

; ------------------------------------------------------------------------------------------------------------------;
; Alias Exports
; ------------------------------------------------------------------------------------------------------------------;
.export text_org
.export sys_break
.export sys_ide
.export sys_io
.export sys_filesystem
.export sys_spawn_proc
.export sys_list
.export sys_rtc
.export sys_datetime
.export sys_reboot
.export sys_pause_proc
.export sys_resume_proc
.export sys_terminate_proc
.export sys_system

; ------------------------------------------------------------------------------------------------------------------;
; IRQs' Code Block
; ------------------------------------------------------------------------------------------------------------------;
int_0:
  sysret
int_1:
  sysret
int_2:
  sysret
int_3:
  sysret
int_4:
  sysret
int_5:
  sysret

; ------------------------------------------------------------------------------------------------------------------;
; Process Swapping
; ------------------------------------------------------------------------------------------------------------------;
int_6:  
  pusha ; save all registers into kernel stack
  mov ah, 0
  mov al, [active_proc_index]
  shl a              ; x2
  mov a, [proc_table_convert + a]  ; get process state start index
  mov di, a
  mov a, sp
  inc a
  mov si, a
  mov c, 20
  rep movsb          ; save process state!
; restore kernel stack position to point before interrupt arrived
  add sp, 20
; now load next process in queue
  mov al, [active_proc_index]
  mov bl, [nbr_active_procs]
  cmp al, bl
  je int6_cycle_back
  inc al            ; next process is next in the series
  jmp int6_continue
int6_cycle_back:
  mov al, 1        ; next process = process 1
int6_continue:
  mov [active_proc_index], al    ; set next active proc

; calculate LUT entry for next process
  mov ah, 0
  shl a              ; x2
  mov a, [proc_table_convert + a]    ; get process state start index  
  
  mov si, a            ; source is proc state block
  mov a, sp
  sub a, 19
  mov di, a            ; destination is kernel stack
; restore SP
  dec a
  mov sp, a
  mov c, 20
  rep movsb
; set VM process
  mov al, [active_proc_index]
  setptb
  mov byte[_TIMER_C_0], 0        ; load counter 0 low byte
  mov byte[_TIMER_C_0], $10        ; load counter 0 high byte
  popa
  sysret

; ------------------------------------------------------------------------------------------------------------------;
; UART0 Interrupt
; ------------------------------------------------------------------------------------------------------------------;
int_7:
  push a
  push d
  pushf
  mov a, [fifo_in]
  mov d, a
  mov al, [_UART0_DATA]  ; get character
  cmp al, $03        ; CTRL-C
  je CTRLC
  cmp al, $1A        ; CTRL-Z
  je CTRLZ
  mov [d], al        ; add to fifo
  mov a, [fifo_in]
  inc a
  cmp a, fifo + FIFO_SIZE         ; check if pointer reached the end of the fifo
  jne int_7_continue
  mov a, fifo  
int_7_continue:  
  mov [fifo_in], a      ; update fifo pointer
  popf
  pop d
  pop a  
  sysret
CTRLC:
  add sp, 5
  jmp syscall_terminate_proc
CTRLZ:
  popf
  pop d
  pop a
  jmp syscall_pause_proc    ; pause current process and go back to the shell

; ------------------------------------------------------------------------------------------------------------------;
; System Syscalls
; ------------------------------------------------------------------------------------------------------------------;
system_jmptbl:
  .dw system_uname
  .dw system_whoami
  .dw system_setparam
  .dw system_bootloader_install
  .dw system_getparam
syscall_system:
  jmp [system_jmptbl + al]

; param register address in register d
; param value in register bl
system_getparam:
  mov bl, [d]
  sysret

; kernel LBA address in 'b'
system_bootloader_install:
  push b
  mov b, 0
  mov c, 0
  mov ah, $01                 ; 1 sector
  mov d, transient_area
  call ide_read_sect          ; read sector
  pop b
  mov [d + 510], b            ; update LBA address
  mov b, 0
  mov c, 0
  mov ah, $01                 ; 1 sector
  mov d, transient_area
  call ide_write_sect         ; write sector
  sysret

; param register address in register d
; param value in register bl
system_setparam:
  mov [d], bl
  sysret

system_uname:
  sysret

system_whoami:
  sysret

; REBOOT SYSTEM
syscall_reboot:
  push word $FFFF 
  push byte %00000000             ; dma_ack = 0, interrupts disabled, mode = supervisor, 
          : paging = off, halt=0, display_reg_load=0, dir=0
  push word BIOS_RESET_VECTOR    ; and then push RESET VECTOR of the shell to the stack
  sysret

;------------------------------------------------------------------------------------------------------;;
; switch to another process
; inputs:
; AL = new process number
;------------------------------------------------------------------------------------------------------;;
syscall_resume_proc:
  mov g, a                            ; save the process number
  pusha                               ; save all registers into kernel stack
  mov ah, 0
  mov al, [active_proc_index]
  shl a              ; x2
  mov a, [proc_table_convert + a]     ; get process state start index
  mov di, a
  mov a, sp
  inc a
  mov si, a
  mov c, 20
  rep movsb                           ; save process state!
; restore kernel stack position to point before interrupt arrived
  add sp, 20
; now load the new process number!
  mov a, g                            ; retrieve the process number argument that was saved in the beginning
  mov [active_proc_index], al         ; set new active proc
; calculate LUT entry for next process
  mov ah, 0
  shl a                               ; x2
  mov a, [proc_table_convert + a]     ; get process state start index  
  mov si, a                           ; source is proc state block
  mov a, sp
  sub a, 19
  mov di, a                           ; destination is kernel stack
; restore SP
  dec a
  mov sp, a
  mov c, 20
  rep movsb
; set VM process
  mov al, [active_proc_index]
  setptb
  popa
  sysret

syscall_list_procs:
  mov d, s_ps_header
  call _puts
  mov d, proc_availab_table + 1
  mov c, 1
list_procs_L0:  
  cmp byte[d], 1
  jne list_procs_next
  mov b, d
  sub b, proc_availab_table
  shl b, 5
  push d
  push b
  mov b, c
  call print_u8x
  mov ah, ' '
  call _putchar
  call _putchar
  pop b
  mov d, b
  add d, proc_names
  call _puts
  call printnl
  pop d
list_procs_next:
  inc d
  inc c
  cmp c, 9
  jne list_procs_L0
list_procs_end:
  sysret

; ------------------------------------------------------------------------------------------------------------------;
; Exceptions' Code Block
; ------------------------------------------------------------------------------------------------------------------;
; Privilege
; ------------------------------------------------------------------------------------------------------------------;
trap_privilege:
  jmp syscall_reboot
  push d
  mov d, s_priviledge
  call _puts
  pop d
  sysret

; ------------------------------------------------------------------------------------------------------------------;
; Breakpoint
; IMPORTANT: values in the stack are being pushed in big endian. i.e.: MSB at low address
; and LSB at high address. *** NEED TO CORRECT THIS IN THE MICROCODE and make it little endian again ***
; ------------------------------------------------------------------------------------------------------------------;
syscall_break:
  pusha
syscall_break_prompt:
  mov d, s_break1
  call _puts
  call printnl
  call scan_u16d
  cmp a, 0
  je syscall_break_regs
  cmp a, 1
  je syscall_break_mem
syscall_break_end:  
  popa
  sysret
syscall_break_regs:
  mov a, sp
  add a, 14               ; back-track 7 registers
  mov d, a
  mov cl, 7
syscall_regs_L0:
  mov b, [d]
  swp b
  call print_u16x         ; print register value
  call printnl
  sub d, 2
  sub cl, 1
  cmp cl, 0
  jne syscall_regs_L0
  jmp syscall_break_prompt
  call printnl
  jmp syscall_break_prompt
syscall_break_mem:
  call printnl
  call scan_u16x
  mov si, a               ; data source from user space
  mov di, scrap_sector    ; destination in kernel space
  mov c, 512
  load                    ; transfer data to kernel space!
  mov d, scrap_sector     ; dump pointer in d
  mov c, 0
dump_loop:
  mov al, cl
  and al, $0F
  jz print_base
back:
  mov al, [d]             ; read byte
  mov bl, al
  call print_u8x
  mov a, $2000
  syscall sys_io          ; space
  mov al, cl
  and al, $0F
  cmp al, $0F
  je print_ascii
back1:
  inc d
  inc c
  cmp c, 512
  jne dump_loop
  call printnl
  jmp syscall_break_prompt  ; go to syscall_break return point
print_ascii:
  mov a, $2000
  syscall sys_io
  sub d, 16
  mov b, 16
print_ascii_L:
  inc d
  mov al, [d]               ; read byte
  cmp al, $20
  jlu dot
  cmp al, $7E
  jleu ascii
dot:
  mov a, $2E00
  syscall sys_io
  jmp ascii_continue
ascii:
  mov ah, al
  mov al, 0
  syscall sys_io
ascii_continue:
  loopb print_ascii_L
  jmp back1
print_base:
  call printnl
  mov b, d
  sub b, scrap_sector      ; remove this later and fix address bases which display incorrectly
  call print_u16x          ; display row
  mov a, $3A00
  syscall sys_io
  mov a, $2000
  syscall sys_io
  jmp back

s_break1:  
  .db "\nDebugger entry point.\n"
  .db "0. Show Registers\n"
  .db "1. Show 512B RAM block\n"
  .db "2. Continue Execution", 0

; ------------------------------------------------------------------------------------------------------------------;
; Divide by Zero
; ------------------------------------------------------------------------------------------------------------------;
trap_div_zero:
  push a
  push d
  pushf
  mov d, s_divzero
  call _puts
  popf
  pop d
  pop a
  sysret ; enable interrupts

; ------------------------------------------------------------------------------------------------------------------;
; Undefined Opcode
; ------------------------------------------------------------------------------------------------------------------;
trap_undef_opcode:
  sysret

; ------------------------------------------------------------------------------------------------------------------;
; RTC Services Syscall
; RTC I/O bank = FFA0 to FFAF
; FFA0 to FFA7 is scratch RAM
; Control register at $FFA8 [ W | R | S | Cal4..Cal0 ]
; al = 0..6 -> get
; al = 7..D -> set
; ------------------------------------------------------------------------------------------------------------------;
syscall_rtc:
  push al
  push d
  cmp al, 6
  jgu syscall_rtc_set
syscall_rtc_get:
  add al, $A9             ; generate RTC address to get to address A9 of clock
  mov ah, $FF    
  mov d, a                ; get to FFA9 + offset
  mov byte[$FFA8], $40    ; set R bit to 1
  mov al, [d]             ; get data
  mov byte[$FFA8], 0      ; reset R bit
  mov ah, al
  pop d
  pop al
  sysret
syscall_rtc_set:
  push bl
  mov bl, ah              ; set data asIDE
  add al, $A2             ; generate RTC address to get to address A9 of clock
  mov ah, $FF    
  mov d, a                ; get to FFA9 + offset
  mov al, bl              ; get data back
  mov byte[$FFA8], $80    ; set W bit to 1
  mov [d], al             ; set data
  mov byte[$FFA8], 0      ; reset write bit
  pop bl
  pop d
  pop al
  sysret

datetime_serv_tbl:
  .dw print_date
  .dw set_date
syscall_datetime:
  jmp [datetime_serv_tbl + al]      
print_date:
  mov a, $0D00           ; print carriage return char
  mov al, 3
  syscall sys_rtc        ; get week
  mov al, ah
  mov ah, 0
  shl a, 2          
  mov d, s_week
  add d, a
  call _puts
  mov a, $2000
  syscall sys_io         ; display ' '
  mov al, 4
  syscall sys_rtc        ; get day
  mov bl, ah
  call print_u8x
  mov a, $2000
  syscall sys_io         ; display ' '
; there is a problem with the month displaying
; the month is stored as BCD. so when retrieving the month, the value will be in binary
; even though it is to be understood as BCD.
; when retrieving the value and adding the string table address offset the value will go overboard!  
  mov al, 05
  syscall sys_rtc        ; get month
  mov al, ah
  mov ah, 0
  shl a, 2          
  mov d, s_months
  add d, a
  call _puts
  mov a, $2000
  syscall sys_io         ; display ' '
  mov bl, $20
  call print_u8x         ; print 20 for year prefix
  mov al, 06
  syscall sys_rtc        ; get year
  mov bl, ah
  call print_u8x
  mov a, $2000  
  syscall sys_io         ; display ' '
  mov al, 2
  syscall sys_rtc        ; get hours
  mov bl, ah
  call print_u8x
  mov a, $3A00    
  syscall sys_io         ; display ':'
  mov al, 01
  syscall sys_rtc        ; get minutes
  mov bl, ah
  call print_u8x
  mov a, $3A00  
  syscall sys_io         ; display ':'
  mov al, 0
  syscall sys_rtc        ; get seconds
  mov bl, ah
  call print_u8x
  call printnl
  sysret
set_date:
  mov d, s_set_year
  call _puts
  call scan_u8x          ; read integer into A
  shl a, 8               ; only AL used, move to AH
  mov al, 0Dh            ; set RTC year
  syscall sys_rtc        ; set RTC
  mov d, s_set_month
  call _puts
  call scan_u8x          ; read integer into A
  shl a, 8               ; only AL used, move to AH
  mov al, 0Ch            ; set RTC month
  syscall sys_rtc        ; set RTC
  mov d, s_set_day
  call _puts
  call scan_u8x          ; read integer into A
  shl a, 8               ; only AL used, move to AH
  mov al, 0Bh            ; set RTC month
  syscall sys_rtc        ; set RTC
  mov d, s_set_week
  call _puts
  call scan_u8x          ; read integer into A
  shl a, 8               ; only AL used, move to AH
  mov al, 0Ah            ; set RTC month
  syscall sys_rtc        ; set RTC
  mov d, s_set_hours
  call _puts
  call scan_u8x          ; read integer into A
  shl a, 8               ; only AL used, move to AH
  mov al, 09h            ; set RTC month
  syscall sys_rtc        ; set RTC
  mov d, s_set_minutes
  call _puts
  call scan_u8x          ; read integer into A
  shl a, 8               ; only AL used, move to AH
  mov al, 08h            ; set RTC month
  syscall sys_rtc        ; set RTC
  mov d, s_set_seconds
  call _puts
  call scan_u8x          ; read integer into A
  shl a, 8               ; only AL used, move to AH
  mov al, 07h            ; set RTC month
  syscall sys_rtc        ; set RTC
  sysret

; ------------------------------------------------------------------------------------------------------------------;
; IDE Services Syscall
; al = option
; 0 = IDE reset, 1 = IDE sleep, 2 = read sector, 3 = write sector
; IDE read/write sector
; 512 bytes
; User buffer pointer in D
; AH = number of sectors
; CB = LBA bytes 3..0
; ------------------------------------------------------------------------------------------------------------------;
s_syscall_ide_dbg0: .db "> syscall_ide called: ", 0
ide_serv_tbl:
  .dw ide_reset
  .dw ide_sleep
  .dw ide_read_sect_wrapper
  .dw ide_write_sect_wrapper
syscall_ide:
  push bl
  mov bl, [sys_debug_mode]
  ; debug block
  cmp bl, 0
  pop bl
  je syscall_ide_jmp
  push d
  push bl
  mov d, s_syscall_ide_dbg0
  call _puts
  mov bl, al
  call print_u8x
  call printnl
  pop bl
  pop d
syscall_ide_jmp:
  jmp [ide_serv_tbl + al]    
  
ide_reset:      
  mov byte[_ide_R7], 4            ; RESET IDE
  call ide_wait                   ; wait for IDE ready             
  mov byte[_ide_R6], $E0          ; LBA3= 0, MASTER, MODE= LBA        
  mov byte[_ide_R1], 1            ; 8-BIT TRANSFERS      
  mov byte[_ide_R7], $EF          ; SET FEATURE COMMAND
  sysret
ide_sleep:
  call ide_wait                   ; wait for IDE ready             
  mov byte [_ide_R6], %01000000   ; lba[3:0](reserved), bit 6=1
  mov byte [_ide_R7], $E6         ; sleep command
  call ide_wait                   ; wait for IDE ready
  sysret
ide_read_sect_wrapper:
  call ide_read_sect
  sysret
ide_write_sect_wrapper:
  call ide_write_sect
  sysret
ide_read_sect:
  mov al, ah
  mov ah, bl
  mov [_ide_R2], a                ; number of sectors (0..255)
  mov al, bh
  mov [_ide_R4], al
  mov a, c
  mov [_ide_R5], al
  mov al, ah
  and al, %00001111
  or al, %11100000                ; mode lba, master
  mov [_ide_R6], al
ide_read_sect_wait:
  mov al, [_ide_R7]  
  and al, $80                     ; BUSY FLAG
  jnz ide_read_sect_wait
  mov al, $20
  mov [_ide_R7], al               ; read sector cmd
  call ide_read  
  ret
ide_write_sect:
  mov al, ah
  mov ah, bl
  mov [_ide_R2], a                ; number of sectors (0..255)
  mov al, bh
  mov [_ide_R4], al
  mov a, c
  mov [_ide_R5], al
  mov al, ah
  and al, %00001111
  or al, %11100000                ; mode lba, master
  mov [_ide_R6], al
ide_write_sect_wait:
  mov al, [_ide_R7]  
  and al, $80                     ; BUSY FLAG
  jnz ide_write_sect_wait
  mov al, $30
  mov [_ide_R7], al               ; write sector cmd
  call ide_write      
  ret

;----------------------------------------------------------------------------------------------------;
; READ IDE DATA
; pointer in D
;----------------------------------------------------------------------------------------------------;
ide_read:
  push d
ide_read_loop:
  mov al, [_ide_R7]  
  and al, 80h                     ; BUSY FLAG
  jnz ide_read_loop               ; wait loop
  mov al, [_ide_R7]
  and al, %00001000               ; DRQ FLAG
  jz ide_read_end
  mov al, [_ide_R0]
  mov [d], al
  inc d
  jmp ide_read_loop
ide_read_end:
  pop d
  ret

;----------------------------------------------------------------------------------------------------;
; WRITE IDE DATA
; data pointer in D
;----------------------------------------------------------------------------------------------------;
ide_write:
  push d
ide_write_loop:
  mov al, [_ide_R7]  
  and al, 80h             ; BUSY FLAG
  jnz ide_write_loop      ; wait loop
  mov al, [_ide_R7]
  and al, %00001000       ; DRQ FLAG
  jz ide_write_end
  mov al, [d]
  mov [_ide_R0], al
  inc d 
  jmp ide_write_loop
ide_write_end:
  pop d
  ret

;----------------------------------------------------------------------------------------------------;
; wait for IDE to be ready
;----------------------------------------------------------------------------------------------------;
ide_wait:
  mov al, [_ide_R7]  
  and al, 80h        ; BUSY FLAG
  jnz ide_wait
  ret

;----------------------------------------------------------------------------------------------------;
; IO Syscall
;----------------------------------------------------------------------------------------------------;
; Baud  Divisor
; 50    2304
; 110   1047
; 300    384
; 600    192
; 1200    96
; 9600    12
; 19200    6
; 38400    3
syscall_io_jmp:
  .dw syscall_io_putchar
  .dw syscall_io_getch
  .dw syscall_io_uart_setup
syscall_io:
  jmp [syscall_io_jmp + al]
; bit7 is the Divisor Latch Access Bit (DLAB). It must be set high (logic 1) to access the Divisor Latches
; of the Baud Generator during a Read or Write operation. It must be set low (logic 0) to access the Receiver
; Buffer, the Transmitter Holding Register, or the Interrupt Enable Register.
syscall_io_uart_setup:
  mov al, [sys_uart0_lcr]
  or al, $80                ; set DLAB access bit
  mov [_UART0_LCR], al      ; 8 data, 2 stop, no parity by default
  mov al, [sys_uart0_div0]
  mov [_UART0_DLAB_0], al   ; divisor latch byte 0
  mov al, [sys_uart0_div1]
  mov [_UART0_DLAB_1], al   ; divisor latch byte 1      

  mov al, [sys_uart0_lcr]
  and al, $7F               ; clear DLAB access bit 
  mov [_UART0_LCR], al
  mov al, [sys_uart0_inten]
  mov [_UART0_IER], al      ; interrupts
  mov al, [sys_uart0_fifoen]
  mov [_UART0_FCR], al      ; FIFO control
  sysret

; char in ah
syscall_io_putchar:
syscall_io_putchar_L0:
  mov al, [_UART0_LSR]         ; read Line Status Register
  and al, $20
  jz syscall_io_putchar_L0    
  mov al, ah
  mov [_UART0_DATA], al        ; write char to Transmitter Holding Register
  sysret

; char in ah
; al = sucess code
syscall_io_getch:
  push b
  push d
  sti
syscall_io_getch_L0:  
  mov a, [fifo_out]
  mov b, [fifo_in]
  cmp a, b
  je syscall_io_getch_L0
  mov d, a
  inc a
  cmp a, fifo + FIFO_SIZE      ; check if pointer reached the end of the fifo
  jne syscall_io_getch_cont
  mov a, fifo  
syscall_io_getch_cont:  
  mov [fifo_out], a             ; update fifo pointer
  mov al, [d]                   ; get char
  mov ah, al
  mov al, [sys_echo_on]
  cmp al, 1
  jne syscall_io_getch_noecho 
; here we just echo the char back to the console
syscall_io_getch_echo_L0:
  mov al, [_UART0_LSR]         ; read Line Status Register
  and al, $20                 ; isolate Transmitter Empty
  jz syscall_io_getch_echo_L0
  mov al, ah
  mov [_UART0_DATA], al        ; write char to Transmitter Holding Register
syscall_io_getch_noecho:
  mov al, 1                    ; AL = 1 means a char successfully received
  pop d
  pop b
  sysret

;------------------------------------------------------------------------------------------------------;
; FILE SYSTEM DATA
;------------------------------------------------------------------------------------------------------;
; infor for : IDE SERVICES INTERRUPT
; IDE read/write 512-byte sector
; al = option
; user buffer pointer in D
; AH = number of sectors
; CB = LBA bytes 3..0  
;------------------------------------------------------------------------------------------------------;
; FILE SYSTEM DATA STRUCTURE
;------------------------------------------------------------------------------------------------------;
; for a directory we have the header first, followed by metadata
; header 1 sector (512 bytes)
; metadata 1 sector (512 bytes)
; HEADER ENTRIES:
; filename (64)
; parent dir LBA (2) -  to be used for faster backwards navigation...
;
; metadata entries:
; filename (24)
; attributes (1)  |_|_|file_type(3bits)|x|w|r| types: file, directory, character device
; LBA (2)
; size (2)
; day (1)
; month (1)
; year (1)
; packet size = 32 bytes
;
; first directory on disk is the root directory '/'
file_system_jmptbl:
  .dw fs_mkfs                   ; 0
  .dw 0                         ; 1
  .dw fs_mkdir                  ; 2
  .dw fs_cd                     ; 3
  .dw fs_ls                     ; 4
  .dw 0                  ; 5
  .dw fs_mkbin                  ; 6
  .dw fs_pwd                    ; 7
  .dw fs_cat                    ; 8
  .dw fs_rmdir                  ; 9
  .dw fs_rm                     ; 10
  .dw fs_starcom                ; 11
  .dw 0                         ; 12
  .dw 0                         ; 13
  .dw fs_chmod                  ; 14
  .dw fs_mv                     ; 15
  .dw fs_cd_root                ; 16
  .dw fs_get_curr_dirID         ; 17
  .dw fs_dir_id_to_path         ; 18
  .dw fs_path_to_dir_id_user    ; 19
  .dw fs_load_from_path_user    ; 20  
  .dw fs_filepath_exists_user   ; 21

s_syscall_fs_dbg0: .db "\n> syscall_file_system called: ", 0
syscall_file_system:
  push bl
  mov bl, [sys_debug_mode]
  ; debug block
  cmp bl, 0
  pop bl
  je syscall_filesystem_jmp
  push d
  push bl
  mov d, s_syscall_fs_dbg0
  call _puts
  mov bl, al
  call print_u8x
  call printnl
  pop bl
  pop d
syscall_filesystem_jmp:
  jmp [file_system_jmptbl + al]

fs_mkfs:  
  sysret  
  
fs_cd_root:
  mov a, root_id
  mov [current_dirID], a      ; set current directory LBA to ROOT
  sysret  

; filename in D (userspace data)
; permission in BL
fs_chmod:
  push bl
  mov si, d
  mov di, user_data
  mov c, 128
  load                        ; load filename from user-space
  mov a, [current_dirID]
  inc a                       ; metadata sector
  mov b, a
  mov c, 0                    ; upper LBA = 0
  mov ah, $01                  ; 1 sector
  mov d, transient_area
  call ide_read_sect          ; read directory
  cla
  mov [index], a              ; reset file counter
fs_chmod_L1:
  mov si, d
  mov di, user_data
  call _strcmp
  je fs_chmod_found_entry
  add d, 32
  mov a, [index]
  inc a
  mov [index], a
  cmp a, FST_FILES_PER_DIR
  jne fs_chmod_L1
  pop bl
  jmp fs_chmod_not_found
fs_chmod_found_entry:  
  mov g, b                    ; save LBA
  pop bl                      ; retrieve saved permission value
  mov al, [d + 24]            ; read file permissions
  and al, %11111000           ; remove all permissions, keep other flags
  or al, bl                   ; set new permissions
  mov [d + 24], al            ; write new permissions
  mov c, 0
  mov d, transient_area
  mov ah, $01                 ; disk write 1 sect
  mov b, g                    ; retrieve LBA
  call ide_write_sect         ; write sector
fs_chmod_not_found:
  sysret

;------------------------------------------------------------------------------------------------------;
; CREATE NEW DIRECTORY
;------------------------------------------------------------------------------------------------------;
; search list for NULL name entry. add new directory to list
fs_mkdir:
  mov si, d
  mov di, user_data
  mov c, 512
  load                        ; load data from user-space
  mov b, FST_LBA_START + 2    ; start at 2 because LBA  0 is ROOT (this would also cause issues                 
                              ; when checking for NULL name, since root has a NULL name)
  mov c, 0                    ; upper LBA = 0
fs_mkdir_L1:  
  mov ah, $01                  ; 1 sector
  mov d, transient_area
  call ide_read_sect          ; read sector
  cmp byte[d], 0              ; check for NULL
  je fs_mkdir_found_null
  add b, FST_SECTORS_PER_DIR  ; skip directory
  jmp fs_mkdir_L1
fs_mkdir_found_null:
;create header file by grabbing dir name from parameter
  push b                      ; save new directory's LBA
  mov c, 64
  mov si, user_data
  mov di, transient_area
  rep movsb                   ; copy dirname from user_data to transient_area
  mov a, [current_dirID]
  mov [transient_area + 64], a    ; store parent directory LBA
  mov al, 0
  mov di, transient_area + 512
  mov c, 512
  rep stosb                       ; clean buffer
  mov c, 0                        ; reset LBA(c) to 0
; write directory entry sectors
  mov d, transient_area
  mov ah, $02                     ; disk write, 2 sectors
  call ide_write_sect             ; write sector
; now we need to add the new directory to the list, insIDE the current directory
  mov a, [current_dirID]
  add a, 1
  mov b, a                        ; metadata sector
  mov c, 0
  mov g, b                        ; save LBA
  mov d, transient_area
  mov ah, $01                  ; 1 sector
  call ide_read_sect              ; read metadata sector
fs_mkdir_L2:
  cmp byte[d], 0
  je fs_mkdir_found_null2
  add d, FST_ENTRY_SIZE
  jmp fs_mkdir_L2                ; we look for a NULL entry here but dont check for limits. 
fs_mkdir_found_null2:
  mov si, user_data
  mov di, d
  call _strcpy                    ; copy directory name
  add d, 24                       ; goto ATTRIBUTES
  mov al, %00001011               ; directory, no execute, write, read
  mov [d], al      
  inc d
  pop b
  push b                          ; push LBA back
  mov [d], b                      ; save LBA
; set file creation date  
  add d, 4
  mov al, 4
  syscall sys_rtc
  mov al, ah
  mov [d], al                     ; set day
  inc d
  mov al, 5
  syscall sys_rtc
  mov al, ah
  mov [d], al                     ; set month
  inc d
  mov al, 6
  syscall sys_rtc
  mov al, ah
  mov [d], al                     ; set year
; write sector into disk for new directory entry
  mov b, g
  mov c, 0
  mov d, transient_area
  mov ah, $01                     ; disk write, 1 sector
  call ide_write_sect             ; write sector

; after adding the new directory's information to its parent directory's list
; we need to now enter the new directory, and to it add two new directories!
; which directories do we need to add ? '..' and '.' are the directories needed.
; importantly, note that these two new directories are only entries in the list
; and do not have actual physical entries in the disk as real directories.
; i.e. they only exist as list entries in the new directory created so that
; the new directory can reference its parent and itself.
; We need to add both '..' and '.'
; this first section is for '..' and on the section below we do the same for '.'
  pop a                         ; retrieve the new directory's LBA  
  push a                        ; and save again
  add a, 1
  mov b, a                      ; metadata sector
  mov c, 0
  mov g, b                      ; save LBA
  mov d, transient_area
  mov ah, $01                  ; 1 sector
  call ide_read_sect            ; read metadata sector
fs_mkdir_L3:
  cmp byte[d], 0
  je fs_mkdir_found_null3
  add d, FST_ENTRY_SIZE
  jmp fs_mkdir_L3              ; we look for a NULL entry here but dont check for limits.
fs_mkdir_found_null3:
  mov si, s_parent_dir
  mov di, d
  call _strcpy                  ; copy directory name
  add d, 24                     ; goto ATTRIBUTES
  mov al, %00001011             ; directory, no execute, write, read, 
  mov [d], al      
  inc d
  mov b, [current_dirID]        ; retrieve the parent directorys LBA
  mov [d], b                    ; save LBA
; set file creation date  
  add d, 4
  mov al, 4
  syscall sys_rtc
  mov al, ah
  mov [d], al                   ; set day
  inc d
  mov al, 5
  syscall sys_rtc
  mov al, ah
  mov [d], al                   ; set month
  inc d
  mov al, 6
  syscall sys_rtc
  mov al, ah
  mov [d], al                   ; set year
; write sector into disk for new directory entry
  mov b, g
  mov c, 0
  mov d, transient_area
  mov ah, $01                   ; disk write, 1 sector
  call ide_write_sect           ; write sector
;;;;;;;;;;;;;
; like we did above for '..', we need to now add the '.' directory to the list.
;------------------------------------------------------------------------------------------------------;
  pop a                         ; retrieve the new directory's LBA  
  push a
  add a, 1
  mov b, a                      ; metadata sector
  mov c, 0
  mov g, b                      ; save LBA
  mov d, transient_area
  mov ah, $01                  ; 1 sector
  call ide_read_sect            ; read metadata sector
fs_mkdir_L4:
  cmp byte[d], 0
  je fs_mkdir_found_null4
  add d, FST_ENTRY_SIZE
  jmp fs_mkdir_L4              ; we look for a NULL entry here but dont check for limits.
fs_mkdir_found_null4:
  mov si, s_current_dir
  mov di, d
  call _strcpy                  ; copy directory name
  add d, 24                     ; goto ATTRIBUTES
  mov al, %00001011             ; directory, no execute, write, read, 
  mov [d], al      
  inc d
  pop b                         ; new directory's LBA itself. for self-referential directory entry '.'
  mov [d], b                    ; save LBA
; set file creation date  
  add d, 4
  mov al, 4
  syscall sys_rtc
  mov al, ah
  mov [d], al                   ; set day
  inc d
  mov al, 5
  syscall sys_rtc
  mov al, ah
  mov [d], al                   ; set month
  inc d
  mov al, 6
  syscall sys_rtc
  mov al, ah
  mov [d], al                   ; set year
; write sector into disk for new directory entry
  mov b, g
  mov c, 0
  mov d, transient_area
  mov ah, $01                   ; disk write, 1 sector
  call ide_write_sect           ; write sector
fs_mkdir_end:
  sysret

;------------------------------------------------------------------------------------------------------;
; get path from a given directory dirID
; pseudo code:
;  fs_dir_id_to_path(int dirID, char *D){
;    if(dirID == 0){
;      reverse path in D;
;      return;
;    }
;    else{
;      copy directory name to end of D;
;      add '/' to end of D;
;      parentID = get parent directory ID;
;      fs_dir_id_to_path(parentID, D);
;    }
;  }
; A = dirID
; D = generated path string pointer
;------------------------------------------------------------------------------------------------------;
; sample path: /usr/bin
fs_dir_id_to_path:
  mov d, filename
  mov al, 0
  mov [d], al                     ; initialize path string 
  mov a, [current_dirID]
  call fs_dir_id_to_path_E0
  mov d, filename
  call _strrev
  call _puts
  sysret
fs_dir_id_to_path_E0:
  call get_dirname_from_dirID
  mov si, s_fslash
  mov di, d
  call _strcat                    ; add '/' to end of path
  cmp a, root_id               ; check if we are at the root directory
  je fs_dir_id_to_path_root
  call get_parentID_from_dirID    ; use current ID (A) to find parentID (into A)
  cmp a, root_id               ; check if we are at the root directory
  je fs_dir_id_to_path_root
  call fs_dir_id_to_path_E0     ; recursively call itself
fs_dir_id_to_path_root:
  ret

;------------------------------------------------------------------------------------------------------;
; in_puts:
; A = directory ID
; out_puts:
; D = pointer to directory name string
;------------------------------------------------------------------------------------------------------;
get_dirname_from_dirID:
  push a
  push b
  push d
  mov b, a
  mov c, 0                      ; upper LBA = 0
  mov ah, $01                  ; 1 sector
  mov d, transient_area - 512
  call ide_read_sect            ; read directory
  call _strrev                  ; reverse dir name before copying
  mov si, d
  pop d                         ; destination address = D value pushed at beginning
  mov di, d
  call _strcat                  ; copy filename to D
  pop b
  pop a
  ret

;------------------------------------------------------------------------------------------------------;
; in_puts:
; A = directory ID
; out_puts:
; A = parent directory ID
;------------------------------------------------------------------------------------------------------;
get_parentID_from_dirID:
  push b
  push d
  mov b, a
  mov c, 0                      ; upper LBA = 0
  mov ah, $01                  ; 1 sector
  mov d, transient_area - 512
  call ide_read_sect            ; read directory
  mov a, [d + 64]               ; copy parent ID value to A
  pop d
  pop b
  ret

;------------------------------------------------------------------------------------------------------;
; get dirID from a given path string
; in_puts:
; D = path pointer 
; out_puts:
; A = dirID
; if dir non existent, A = FFFF (fail code)
; /usr/local/bin    - absolute
; local/bin/games    - relative
;------------------------------------------------------------------------------------------------------;
fs_path_to_dir_id_user:
  mov si, d
  mov di, user_data
  mov c, 512
  load
  call get_dirID_from_path
  sysret
get_dirID_from_path:
  mov b, user_data
  mov [prog], b                  ; token pointer set to path string
  call get_token
  mov bl, [tok]
  cmp bl, TOK_FSLASH
  je get_dirID_from_path_abs 
  mov a, [current_dirID]
  call _putback
  jmp get_dirID_from_path_E0
get_dirID_from_path_abs:
  mov a, root_id
get_dirID_from_path_E0:
  call get_token
  mov bl, [toktyp]
  cmp bl, TOKTYP_IDENTIFIER
  jne get_dirID_from_path_end   ; check if there are tokens after '/'. i.e. is this a 'cd /' command?

  mov si, tokstr
  mov di, filename
  call _strcpy        
  inc a                         ; metadata sector
  mov b, a
  mov c, 0                      ; upper LBA = 0
  mov ah, $01                  ; 1 sector
  mov d, transient_area
  call ide_read_sect            ; read directory
  cla
  mov [index], a
get_dirID_from_path_L1:
  mov si, d
  mov di, filename
  call _strcmp
  je get_dirID_from_path_name_equal  
  add d, 32
  mov a, [index]
  inc a
  mov [index], a
  cmp a, FST_FILES_PER_DIR
  je get_dirID_from_path_fail
  jmp get_dirID_from_path_L1
get_dirID_from_path_name_equal:
  add d, 25           
  mov a, [d]                    ; set result register A = dirID
  call get_token
  mov bl, [tok]
  cmp bl, TOK_FSLASH            ; check if there are more elements in the path
  je get_dirID_from_path_E0
  call _putback
get_dirID_from_path_end:
  ret
get_dirID_from_path_fail:
  mov A, $FFFF
  ret


;------------------------------------------------------------------------------------------------------;
; check if file exists by a given path string
; in_puts:
; D = path pointer 
; OUTPUTS:
; A = success code, if file exists gives LBA, else, give 0
; /usr/local/bin/ed
;------------------------------------------------------------------------------------------------------;
fs_filepath_exists_user:
  mov si, d
  mov di, user_data
  mov c, 512
  load
  call file_exists_by_path
  sysret
file_exists_by_path:
  mov b, user_data
  mov [prog], b                   ; token pointer set to path string
  call get_token
  mov bl, [tok]
  cmp bl, TOK_FSLASH
  je  file_exists_by_path_abs
  mov a, [current_dirID]
  call _putback
  jmp file_exists_by_path_E0
file_exists_by_path_abs:
  mov a, root_id
file_exists_by_path_E0:
  call get_token
  mov bl, [toktyp]
  cmp bl, TOKTYP_IDENTIFIER
  jne file_exists_by_path_end     ; check if there are tokens after '/'
  mov si, tokstr
  mov di, filename
  call _strcpy        
  inc a                           ; metadata sector
  mov b, a
  mov c, 0                        ; upper LBA = 0
  mov ah, $01                  ; 1 sector
  mov d, transient_area
  call ide_read_sect              ; read directory
  cla
  mov [index], a
file_exists_by_path_L1:
  mov si, d
  mov di, filename
  call _strcmp
  je   file_exists_by_path_name_equal
  add d, 32
  mov a, [index]
  inc a
  mov [index], a
  cmp a, FST_FILES_PER_DIR
  je file_exists_by_path_end
  jmp file_exists_by_path_L1
file_exists_by_path_name_equal:
  mov bl, [d + 24]
  and bl, %00111000               ; directory flag
  cmp bl, %00001000               ; is dir?
  je file_exists_by_path_isdir;
; entry is a file
  mov a, [d + 25]                 ; get and return LBA of file
  ret
file_exists_by_path_isdir:
  add d, 25           
  mov a, [d]                      ; set result register A = dirID
  call get_token
  jmp file_exists_by_path_E0
file_exists_by_path_end:
  mov a, 0                        ; return 0 because file was not found
  ret

;------------------------------------------------------------------------------------------------------;
; load file data from a given path string
; inputs:
; D = path pointer 
; DI = userspace program data destination
; /usr/local/bin/ed
; ./ed
;------------------------------------------------------------------------------------------------------;
fs_load_from_path_user:
  push di
  mov si, d
  mov di, user_data
  mov c, 512
  load
  call loadfile_from_path
  pop di
  mov si, transient_area
  mov c, 512 * (FS_SECTORS_PER_FILE-1)
  store
  sysret
loadfile_from_path:
  mov b, user_data
  mov [prog], b                 ; token pointer set to path string
  call get_token
  mov bl, [tok]
  cmp bl, TOK_FSLASH
  je loadfile_from_path_abs 
  mov a, [current_dirID]
  call _putback
  jmp loadfile_from_path_E0
loadfile_from_path_abs:
  mov a, root_id
loadfile_from_path_E0:
  call get_token
  mov bl, [toktyp]
  cmp bl, TOKTYP_IDENTIFIER
  jne loadfile_from_path_end    ; check if there are tokens after '/'. i.e. is this a 'cd /' command?
  mov si, tokstr
  mov di, filename
  call _strcpy        
  inc a                         ; metadata sector
  mov b, a
  mov c, 0                      ; upper LBA = 0
  mov ah, $01                  ; 1 sector
  mov d, transient_area
  call ide_read_sect            ; read directory
  cla
  mov [index], a
loadfile_from_path_L1:
  mov si, d
  mov di, filename
  call _strcmp
  je loadfile_from_path_name_equal  
  add d, 32
  mov a, [index]
  inc a
  mov [index], a
  cmp a, FST_FILES_PER_DIR
  je loadfile_from_path_end
  jmp loadfile_from_path_L1
loadfile_from_path_name_equal:
  mov bl, [d + 24]
  and bl, %00111000             ; directory flag
  cmp bl, %00001000             ; is dir?
  je loadfile_isdirectory  
; entry is a file
  mov b, [d + 25]               ; get LBA
  inc b                         ; add 1 to B because the LBA for data comes after the header sector
  mov d, transient_area
  mov c, 0
  mov ah, FS_SECTORS_PER_FILE-1 ; number of sectors
  call ide_read_sect            ; read sector
  ret
loadfile_isdirectory:
  add d, 25           
  mov a, [d]                    ; set result register A = dirID
  call get_token
  jmp loadfile_from_path_E0
loadfile_from_path_end:
  ret

;------------------------------------------------------------------------------------------------------;
; return the ID of the current directory
; ID returned in B
;------------------------------------------------------------------------------------------------------;
fs_get_curr_dirID:
  mov b, [current_dirID]
  sysret

;------------------------------------------------------------------------------------------------------;
; CD
;------------------------------------------------------------------------------------------------------;
; new dirID in B
fs_cd:
  mov [current_dirID], b
  sysret  

;------------------------------------------------------------------------------------------------------;
; LS
; dirID in B
;------------------------------------------------------------------------------------------------------;
ls_count:       .dw 0
fs_ls:
  inc b                        ; metadata sector
  mov c, 0                     ; upper LBA = 0
  mov ah, $01                  ; 1 sector
  mov d, transient_area
  call ide_read_sect           ; read directory
  cla
  mov [index], a               ; reset entry index
  mov [ls_count], al           ; reset item count
fs_ls_L1:
  cmp byte [d], 0              ; check for NULL
  je fs_ls_next
fs_ls_non_null:
  mov al, [ls_count]
  inc al
  mov [ls_count], al           ; increment item count
  mov al, [d + 24]
  and al, %00111000
  shr al, 3
  mov ah, 0                    ; file type
  mov a, [a + file_type]      
  mov ah, al
  call _putchar
  mov al, [d + 24]
  and al, %00000001
  mov ah, 0
  mov a, [a + file_attrib]     ; read
  mov ah, al
  call _putchar
  mov al, [d + 24]
  and al, %00000010
  mov ah, 0
  mov a, [a + file_attrib]     ; write
  mov ah, al
  call _putchar
  mov al, [d + 24]
  and al, %00000100
  mov ah, 0
  mov a, [a + file_attrib]     ; execute
  mov ah, al
  call _putchar
  mov ah, $20
  call _putchar  
  mov a, [d + 27]
  call print_u16d              ; filesize
  mov ah, $20
  call _putchar  
  mov a, [d + 25]
  call print_u16d              ; dirID / LBA
  mov ah, $20
  call _putchar
; print date
  mov bl, [d + 29]             ; day
  call print_u8x
  mov ah, $20
  call _putchar  
  mov al, [d + 30]             ; month
  shl al, 2
  push d
  mov d, s_months
  mov ah, 0
  add d, a
  call _puts
  pop d
  mov ah, $20
  call _putchar
  mov bl, $20
  call print_u8x
  mov bl, [d + 31]             ; year
  call print_u8x  
  mov ah, $20
  call _putchar  
  call _puts                   ; print filename  
  call printnl
fs_ls_next:
  mov a, [index]
  inc a
  mov [index], a
  cmp a, FST_FILES_PER_DIR
  je fs_ls_end
  add d, 32      
  jmp fs_ls_L1  
fs_ls_end:
  mov d, s_ls_total
  call _puts
  mov al, [ls_count]
  call print_u8d
  call printnl
  sysret


; file structure:
; 512 bytes header
; header used to tell whether the block is free
;------------------------------------------------------------------------------------------------------;
; CREATE NEW TEXTFILE
;------------------------------------------------------------------------------------------------------;
; d = content pointer in user space
; c = file size
fs_starcom:
  mov si, d
  mov di, transient_area
  add c, 512   ; add 512 to c to include file header which contains the filename
  load          ; load data from user-space
  call fs_find_empty_block  ; look for empty data blocks
  push b        ; save empty block LBA
  mov g, b
;create header file by grabbing file name from parameter  
  mov d, transient_area + 512      ; pointer to file contents
  push c              ; save length
  mov al, 1
  mov [transient_area], al          ; mark sectors as USED (not NULL)
  mov d, transient_area
  mov a, c
  mov b, 512
  div a, b
  inc b         ; inc b as the division will most likely have a remainder
  mov ah, bl    ; number of sectors to write, which is the result of
                                ; the division of file size / 512 (small enough to fit in bl)
  mov c, 0      ; lba 
  mov b, g      ; lba 
  call ide_write_sect      ; write sectors
; now we add the file to the current directory!
fs_starcom_add_to_dir:  
  mov a, [current_dirID]
  inc a
  mov b, a          ; metadata sector
  mov c, 0
  mov g, b          ; save LBA
  mov d, scrap_sector
  mov ah, $01        ; 1 sector
  call ide_read_sect    ; read metadata sector
fs_starcom_add_to_dir_L2:
  cmp byte[d], 0
  je fs_starcom_add_to_dir_null
  add d, FST_ENTRY_SIZE
  jmp fs_starcom_add_to_dir_L2    ; we look for a NULL entry here but dont check for limits. 
fs_starcom_add_to_dir_null:
  mov si, transient_area + 1    ; filename located after the data block 'USED' marker byte
  mov di, d
  call _strcpy      ; copy file name
  add d, 24      ; skip name
  mov al, %00000111  ; type=file, execute, write, read
  mov [d], al      
  add d, 3
  pop a
  sub a, 512
  mov [d], a ; file size
  sub d, 2
  pop b        ; get file LBA
  mov [d], b      ; save LBA  
; set file creation date  
  add d, 4
  mov al, 4
  syscall sys_rtc
  mov al, ah
  mov [d], al      ; set day
  inc d
  mov al, 5
  syscall sys_rtc
  mov al, ah
  mov [d], al      ; set month
  inc d
  mov al, 6
  syscall sys_rtc
  mov al, ah
  mov [d], al      ; set year
; write sector into disk for new directory entry
  mov b, g
  mov c, 0
  mov d, scrap_sector
  mov ah, $01      ; disk write, 1 sector
  call ide_write_sect    ; write sector
  sysret

;------------------------------------------------------------------------------------------------------;
; finds an empty data block
; block LBA returned in B
;------------------------------------------------------------------------------------------------------;
fs_find_empty_block:
  mov b, FS_LBA_START     ; raw files starting block
  mov c, 0                ; upper LBA = 0
fs_find_empty_block_L1:  
  mov ah, $01                  ; 1 sector
  mov d, transient_area - 512
  call ide_read_sect      ; read sector
  cmp byte [d], 0
  je fs_find_empty_block_found_null
  add b, FS_SECTORS_PER_FILE
  jmp fs_find_empty_block_L1
fs_find_empty_block_found_null:
  ret

;------------------------------------------------------------------------------------------------------;
; CREATE NEW BINARY FILE
;------------------------------------------------------------------------------------------------------;
; search for first null block
fs_mkbin:
  mov al, 0
  mov [sys_echo_on], al ; disable echo
  mov si, d
  mov di, user_data
  mov c, 512
  load                          ; load data from user-space
  mov b, FS_LBA_START           ; files start when directories end
  mov c, 0                      ; upper LBA = 0
fs_mkbin_L1:  
  mov ah, $01                  ; 1 sector
  mov d, transient_area
  call ide_read_sect            ; read sector
  cmp byte[d], 0                ; check for NULL
  je fs_mkbin_found_null
  add b, FS_SECTORS_PER_FILE
  jmp fs_mkbin_L1
fs_mkbin_found_null:
  push b                        ; save LBA
;create header file by grabbing file name from parameter
  mov di, transient_area + 512  ; pointer to file contents
  call _load_hex                ; load binary hex
  push c                        ; save size (nbr of bytes)
  mov al, 1
  mov [transient_area], al      ; mark sectors as USED (not NULL)
  cla
  mov [index], a
  mov d, transient_area
  mov a, d
  mov [buffer_addr], a
fs_mkbin_L2:
  mov c, 0
  mov ah, $01                   ; disk write, 1 sector
  call ide_write_sect           ; write sector
  mov a, [index]
  inc a
  mov [index], a
  cmp a, FS_SECTORS_PER_FILE    ; remove 1 from this because we dont count the header sector
  je fs_mkbin_add_to_dir
  inc b
  mov a, [buffer_addr]
  add a, 512
  mov [buffer_addr], a
  mov d, a
  jmp fs_mkbin_L2
; now we add the file to the current directory!
fs_mkbin_add_to_dir:  
  mov a, [current_dirID]
  inc a
  mov b, a                      ; metadata sector
  mov c, 0
  mov g, b                      ; save LBA
  mov d, transient_area
  mov ah, $01                  ; 1 sector
  call ide_read_sect            ; read metadata sector
fs_mkbin_add_to_dir_L2:
  cmp byte[d], 0
  je fs_mkbin_add_to_dir_null
  add d, FST_ENTRY_SIZE
  jmp fs_mkbin_add_to_dir_L2   ; we look for a NULL entry here but dont check for limits.
fs_mkbin_add_to_dir_null:
  mov si, user_data
  mov di, d
  call _strcpy                  ; copy file name
  add d, 24                     ; skip name
  mov al, %00000011             ; type=file, no execute, write, read, 
  mov [d], al
  add d, 3
  pop a
  mov [d], a
  sub d, 2
  pop b                         ; get file LBA
  mov [d], b                    ; save LBA
  ; set file creation date  
  add d, 4
  mov al, 4
  syscall sys_rtc
  mov al, ah
  mov [d], al                   ; set day
  inc d
  mov al, 5
  syscall sys_rtc
  mov al, ah
  mov [d], al                   ; set month
  inc d
  mov al, 6
  syscall sys_rtc
  mov al, ah
  mov [d], al                   ; set year
; write sector into disk for new directory entry
  mov b, g
  mov c, 0
  mov d, transient_area
  mov ah, $01                   ; disk write, 1 sector
  call ide_write_sect           ; write sector
  mov al, 1
  mov [sys_echo_on], al ; enable echo
  sysret

;------------------------------------------------------------------------------------------------------;
; PWD - PRINT WORKING DIRECTORY
;------------------------------------------------------------------------------------------------------;    
fs_pwd:
  mov d, filename
  mov al, 0
  mov [d], al                   ; initialize path string 
  mov a, [current_dirID]
  call fs_dir_id_to_path_E0
  mov d, filename
  call _strrev
  call _puts
  call printnl
  sysret

;------------------------------------------------------------------------------------------------------;
; get current directory LBA
; A: returned LBA
;------------------------------------------------------------------------------------------------------;
cmd_get_curr_dir_LBA:
  mov a, [current_dirID]
  sysret

;------------------------------------------------------------------------------------------------------;
; CAT
; userspace destination data pointer in D
; filename starts at D, but is overwritten after the read is made
;------------------------------------------------------------------------------------------------------;:
fs_cat:
  push d                              ; save userspace file data destination
  mov si, d
  mov di, user_data
  mov c, 512
  load                                ; copy filename from user-space
  mov b, [current_dirID]
  inc b                               ; metadata sector
  mov c, 0                            ; upper LBA = 0
  mov ah, $01                  ; 1 sector
  mov d, transient_area-512
  call ide_read_sect                  ; read directory
  cla
  mov [index], a                      ; reset file counter
fs_cat_L1:
  mov si, d
  mov di, user_data
  call _strcmp
  je fs_cat_found_entry
  add d, 32
  mov a, [index]
  inc a
  mov [index], a
  cmp a, FST_FILES_PER_DIR
  je fs_cat_not_found
  jmp fs_cat_L1
fs_cat_found_entry:
  add d, 25                           ; get to dirID of file in disk
  mov b, [d]                          ; get LBA
  inc b                               ; add 1 to B because the LBA for data comes after the header sector 
  mov d, transient_area  
  mov c, 0
  mov ah, FS_SECTORS_PER_FILE-1       ; nbr sectors
  call ide_read_sect                  ; read sectors
  pop di                              ; write userspace file data destination to DI
  mov si, transient_area              ; data origin
  mov c, 512*(FS_SECTORS_PER_FILE-1)
  store
  sysret
fs_cat_not_found:
  pop d
  sysret

;------------------------------------------------------------------------------------------------------;
; RMDIR - remove DIR by dirID
;------------------------------------------------------------------------------------------------------;
; deletes a directory entry in the given directory's file list 
; also deletes the actual directory entry in the FST
; synopsis: rmdir /usr/local/testdir
; B = dirID
fs_rmdir:
  mov g, b
  mov a, b
  call get_parentID_from_dirID  ; now get the directory's parent, in A
  push a                        ; save dirID
; search for directory's entry in the parent's directory then and delete it
  inc a                         ; metadata sector
  mov b, a
  mov c, 0                      ; upper LBA = 0
  mov ah, $01          ;
  mov d, transient_area
  call ide_read_sect            ; read directory
  cla
  mov [index], a                ; reset file counter
  mov b, g                      ; retrieve directory's dirID
fs_rmdir_L1:
  mov a, [d + 25]               ; get entry's dirID/LBA value
  cmp a, b                      ; compare dirID's to find the directory
  je fs_rmdir_found_entry
  add d, 32
  mov a, [index]
  inc a
  mov [index], a
  cmp a, FST_FILES_PER_DIR
  je fs_rmdir_not_found
  jmp fs_rmdir_L1
fs_rmdir_found_entry:
  cla
  mov [d], al                   ; make filename NULL
  mov [d + 25], a               ; clear dirID/LBA as well not to generate problems 
                                ; with previously deleted directories
  pop b
  inc b                         ; metadata sector
  mov c, 0                      ; upper LBA = 0
  mov ah, $01          ; 
  mov d, transient_area
  call ide_write_sect           ; write sector and erase file's entry in the current DIR

  mov b, g
  mov d, transient_area  
  cla
  mov [d], al                   ; make directory's name header NULL for re-use
  mov c, 0
  mov ah, $01                   ; disk write 1 sect
  call ide_write_sect           ; delete directory given by dirID in B
  sysret
fs_rmdir_not_found:
  pop b
  sysret

;------------------------------------------------------------------------------------------------------;
; RM - remove file
;------------------------------------------------------------------------------------------------------;
; frees up the data sectors for the file further down the disk
; deletes file entry in the directory's file list 
fs_rm:
  mov si, d
  mov di, user_data
  mov c, 512
  load                          ; load data from user-space
  mov a, [current_dirID]
  inc a                         ; metadata sector
  mov b, a
  mov c, 0                      ; upper LBA = 0
  mov ah, $01                  ; 1 sector
  mov d, transient_area
  call ide_read_sect            ; read directory
  mov a, 0
  mov [index], a                ; reset file counter
fs_rm_L1:
  mov si, d
  mov di, user_data
  call _strcmp
  je fs_rm_found_entry
  add d, 32
  mov a, [index]
  inc a
  mov [index], a
  cmp a, FST_FILES_PER_DIR
  je fs_rm_not_found
  jmp fs_rm_L1
fs_rm_found_entry:
  mov b, [d + 25]               ; get LBA
  mov g, b                      ; save LBA
  mov al, 0
  mov [d], al                   ; make file entry NULL
  mov a, [current_dirID]
  inc a                         ; metadata sector
  mov b, a
  mov c, 0                      ; upper LBA = 0
  mov ah, $01                   ; disk write
  mov d, transient_area
  call ide_write_sect           ; write sector and erase file's entry in the current DIR
  mov d, transient_area  
  mov al, 0
  mov [d], al                   ; make file's data header NULL for re-use
  mov c, 0
  mov b, g                      ; get data header LBA
  mov ah, $01                   ; disk write 1 sect
  call ide_write_sect           ; write sector
fs_rm_not_found:  
  sysret  

;------------------------------------------------------------------------------------------------------;
; mv - move / change file name
;------------------------------------------------------------------------------------------------------;
fs_mv:
  mov si, d
  mov di, user_data
  mov c, 512
  load                          ; load data from user-space
  mov a, [current_dirID]
  inc a                         ; metadata sector
  mov b, a  
  mov c, 0                      ; upper LBA = 0
  mov ah, $01                  ; 1 sector
  mov d, transient_area
  call ide_read_sect            ; read directory
  cla
  mov [index], a                ; reset file counter
fs_mv_L1:
  mov si, d
  mov di, user_data
  call _strcmp
  je fs_mv_found_entry
  add d, 32
  mov a, [index]
  inc a
  mov [index], a
  cmp a, FST_FILES_PER_DIR
  je fs_mv_not_found
  jmp fs_mv_L1
fs_mv_found_entry:  
  push d
  mov si, user_data + 128       ; (0...127) = original filename , (128...255) = new name
  mov di, d
  call _strcpy  
  mov c, 0
  mov d, transient_area
  mov ah, $01                   ; disk write 1 sect
  call ide_write_sect           ; write sector
  pop d
;; need to check whether its a dir or a file here ;;;
  mov b, [d + 25]               ; get the dirID of the directory so we can locate its own entry in the list
  mov ah, $01
  mov d, transient_area
  mov c, 0
  call ide_read_sect            ; read directory entry
  mov si, user_data + 128
  mov di, d
  call _strcpy                  ; change directory's name
  mov ah, $01
  call ide_write_sect           ; rewrite directory back to disk
fs_mv_not_found:
  sysret

kernel_reset_vector:  
  mov bp, STACK_BEGIN
  mov sp, STACK_BEGIN
  
  mov al, %10000000
  stomsk                        ; mask out timer interrupt for now (only allow UART to interrupt)
  sti  

  lodstat
  and al, %11011111             ; disable display register loading
  stostat
  
; reset fifo pointers
  mov a, fifo
  mov d, fifo_in
  mov [d], a
  mov d, fifo_out
  mov [d], a  
  mov al, 2
  syscall sys_io                ; enable uart in interrupt mode
  
  mov d, s_kernel_started
  call _puts

  mov al, 16
  syscall sys_filesystem        ; set root dirID

  mov d, s_prompt_init
  call _puts
  mov d, s_init_path
  syscall sys_spawn_proc              ; launch init as a new process

;----------------------------------------------------------------------------------------------------;
; Process Index in A
;----------------------------------------------------------------------------------------------------;
find_free_proc:
  mov si, proc_availab_table + 1      ; skip process 0 (kernel)
find_free_proc_L0:
  lodsb                               ; get process state
  cmp al, 0
  je find_free_proc_free              ; if free, jump
  jmp find_free_proc_L0               ; else, goto next
find_free_proc_free:
  mov a, si
  sub a, 1 + proc_availab_table       ; get process index
  ret
  

;----------------------------------------------------------------------------------------------------;
; Process Index in AL
;----------------------------------------------------------------------------------------------------;
proc_memory_map:
  mov ah, 0
  mov b, a                      ; page in BL, 0 in BH
  shl a, 5                      ; multiply by 32
  mov c, a                      ; save in C
  add c, 32
proc_memory_map_L0:
  pagemap
  add b, $0800                  ; increase page number (msb 5 bits of BH only)
  add a, 1                      ; increase both 
  cmp a, c                      ; check to see if we reached the end of memory
  jne proc_memory_map_L0
  ret
  

syscall_terminate_proc:
  add sp, 5                            ; clear stack of the values that were pushed by the interrupt (SP, Status, PC)
                                       ; since they will not be used for anything here.
  mov al, [active_proc_index]
  mov ah, 0  
  shl a, 5                             ; x32
  add a, proc_names
  mov d, a
  mov al, 0
  mov [d], al                           ; nullify process name

  mov al, [active_proc_index]
  mov ah, 0  
  mov d, a
  mov al, 0
  mov [d + proc_availab_table], al    ; make process empty again
  
  mov al, [nbr_active_procs]          ; decrease nbr of active processes
  dec al
  mov [nbr_active_procs], al

; now load the shell process again
  mov al, 2                           ; next process = process 2 = shell
  mov [active_proc_index], al         ; set next active proc

; calculate LUT entry for next process
  mov ah, 0
  shl a                               ; x2
  mov a, [proc_table_convert + a]     ; get process state start index  
  
  mov si, a                           ; source is proc state block
  mov a, sp
  sub a, 19
  mov di, a                           ; destination is kernel stack
; restore SP
  dec a
  mov sp, a
  mov c, 20
  rep movsb
; set VM process
  mov al, [active_proc_index]
  setptb
    
  popa
  sysret

syscall_pause_proc:
; save all registers into kernel stack
  pusha
  mov ah, 0
  mov al, [active_proc_index]
  shl a              ; x2
  mov a, [proc_table_convert + a]   ; get process state start index
    
  mov di, a
  mov a, sp
  inc a
  mov si, a
  mov c, 20
  rep movsb                         ; save process state!
; restore kernel stack position to point before interrupt arrived
  add sp, 20
; now load the shell process again
  mov al, 2                         ; next process = process 2 = shell
  mov [active_proc_index], al       ; set next active proc

; calculate LUT entry for next process
  mov ah, 0
  shl a                             ; x2
  mov a, [proc_table_convert + a]   ; get process state start index  
  
  mov si, a                         ; source is proc state block
  mov a, sp
  sub a, 19
  mov di, a                         ; destination is kernel stack
; restore SP
  dec a
  mov sp, a
  mov c, 20
  rep movsb
; set VM process
  mov al, [active_proc_index]
  setptb
    
  popa
  sysret

;----------------------------------------------------------------------------------------------------;
; spawn a new process
; D = path of the process file to be spawned
; B = arguments ptr
;----------------------------------------------------------------------------------------------------;
syscall_spawn_proc:
; we save the active process first  
  pusha
  mov ah, 0
  mov al, [active_proc_index]
  shl a              ; x2
  mov a, [proc_table_convert + a]    ; get process state table's start index
  
  mov di, a
  mov a, sp
  inc a
  mov si, a
  mov c, 20
  rep movsb                          ; save process state!
; restore kernel stack position to point before interrupt arrived
  add sp, 20
  
  mov si, d                          ; copy the file path
  mov di, user_data
  mov c, 512
  load
  mov a, b
  mov si, a                          ; copy the arguments
  mov di, scrap_sector
  mov c, 512
  load
  call loadfile_from_path            ; load the process file from disk by path (path is in user_data)
                                     ; the file data is loaded into transient_area
; now we allocate a new process  
  call find_free_proc                ; index in A
  setptb 
  call proc_memory_map               ; map process memory pages
; copy arguments into process's memory
  mov si, scrap_sector
  mov di, 0
  mov c, 512
  store
; now copy process binary data into process's memory
  mov si, transient_area
  mov di, text_org              ; code origin address for all user processes
  mov c, FS_FILE_SIZE                ; size of memory space to copy, which is equal to the max file size in disk (for now)
  store                              ; copy process data
    
  call find_free_proc                ; index in A
  mov [active_proc_index], al        ; set new active process
  shl a, 5                           ; x32
  add a, proc_names
  mov di, a
  mov si, user_data                  ; copy and store process filename
  call _strcpy
  
  call find_free_proc                ; index in A
  mov d, a
  mov al, 1
  mov [d + proc_availab_table], al   ; make process busy
  
  mov al, [nbr_active_procs]         ; increase nbr of active processes
  inc al
  mov [nbr_active_procs], al
; launch process
  push word $FFFF 
  push byte %00001110                ; dma_ack = 0, interrupts enabled = 1, mode = user, 
                                     ; paging = on, halt=0, display_reg_load=0, dir=0
  push word text_org
  sysret

proc_table_convert:
  .dw proc_state_table + 0
  .dw proc_state_table + 20
  .dw proc_state_table + 40
  .dw proc_state_table + 60
  .dw proc_state_table + 80
  .dw proc_state_table + 100
  .dw proc_state_table + 120
  .dw proc_state_table + 140
  
;----------------------------------------------------------------------------------------------;
; GET HEX FILE
; di = destination address
; return length in bytes in C
;----------------------------------------------------------------------------------------------;
_load_hex:
  push a
  push b
  push d
  push si
  push di
  mov c, 0
  mov a, di
  mov d, a          ; start of string data block
  call _gets        ; get program string
  mov si, a
__load_hex_loop:
  lodsb             ; load from [SI] to AL
  cmp al, 0         ; check if ASCII 0
  jz __load_hex_ret
  mov bh, al
  lodsb
  mov bl, al
  call _atoi        ; convert ASCII byte in B to int (to AL)
  stosb             ; store AL to [DI]
  inc c
  jmp __load_hex_loop
__load_hex_ret:
  pop di
  pop si
  pop d
  pop b
  pop a
  ret

; synopsis: look insIDE a certain DIRECTORY for files/directories
; BEFORE CALLING THIS FUNCTION, CD INTO REQUIRED DIRECTORY
; for each entry insIDE DIRECTORY:
;  if entry is a file:
;    compare filename to searched filename
;    if filenames are the same, print filename
;  else if entry is a directory:
;    cd to the given directory
;    recursively call cmd_find
;    cd outsIDE previous directory
;  if current entry == last entry, return
; endfor
f_find:
  ret

; FILE INCLUDES
.include "bios.exp"         ; to obtain the BIOS_RESET_VECTOR location (for reboots)
.include "lib/stdio.asm"
.include "lib/ctype.asm"
.include "lib/token.asm"

; Kernel parameters
sys_debug_mode:     .db 0   ; debug modes: 0=normal mode, 1=debug mode
sys_echo_on:        .db 1
sys_uart0_lcr:      .db $07 ; 8 data bits, 2 stop bit, no parity
sys_uart0_inten:    .db 1
sys_uart0_fifoen:   .db 0
sys_uart0_div0:     .db 12  ;
sys_uart0_div1:     .db 0   ; default baud = 9600

nbr_active_procs:   .db 0
active_proc_index:  .db 1

index:              .dw 0
buffer_addr:        .dw 0

fifo_in:            .dw fifo
fifo_out:           .dw fifo

; file system variables
current_dirID:      .dw 0     ; keep dirID of current directory
s_init_path:        .db "/sbin/init", 0

s_parent_dir:       .db "..", 0
s_current_dir:      .db ".", 0
s_fslash:           .db "/", 0
file_attrib:        .db "-rw x"      ; chars at powers of 2
file_type:          .db "-dc"
s_ps_header:        .db "PID COMMAND\n", 0
s_ls_total:         .db "Total: ", 0

s_int_en:           .db "IRQs enabled\n", 0
s_kernel_started:   .db "kernel started\n", 0
s_prompt_init:      .db "starting init\n", 0
s_priviledge:       .db "\nexception: privilege\n", 0
s_divzero:          .db "\nexception: zero division\n", 0

s_set_year:         .db "Year: ", 0
s_set_month:        .db "Month: ", 0
s_set_day:          .db "Day: ", 0
s_set_week:         .db "Weekday: ", 0
s_set_hours:        .db "Hours: ", 0
s_set_minutes:      .db "Minutes: ", 0
s_set_seconds:      .db "Seconds: ", 0
s_months:      
  .db "   ", 0
  .db "Jan", 0
  .db "Feb", 0
  .db "Mar", 0
  .db "Apr", 0
  .db "May", 0
  .db "Jun", 0
  .db "Jul", 0
  .db "Aug", 0
  .db "Sep", 0
  .db "Oct", 0
  .db "Nov", 0
  .db "Dec", 0

s_week:        
  .db "Sun", 0 
  .db "Mon", 0 
  .db "Tue", 0 
  .db "Wed", 0 
  .db "Thu", 0 
  .db "Fri", 0 
  .db "Sat", 0

proc_state_table:   .fill 16 * 20, 0  ; for 15 processes max
proc_availab_table: .fill 16, 0       ; space for 15 processes. 0 = process empty, 1 = process taken
proc_names:         .fill 16 * 32, 0  ; process names
filename:           .fill 128, 0      ; holds a path for file search
user_data:          .fill 512, 0      ;  user space data
fifo:               .fill FIFO_SIZE

scrap_sector:       .fill 512         ; scrap sector
transient_area:     .db 0             ; beginning of the transient memory area. used for disk reads and other purposes    


.end


Paulo Constantino - CC BY-NC-SA 4.0 DEED Attribution-NonCommercial-ShareAlike 4.0 International