live-bootstrap/kernel-bootstrap/builder-hex0-x86.hex0

2440 lines
73 KiB
Plaintext

# SPDX-FileCopyrightText: 2023 Richard Masters <grick23@gmail.com>
# SPDX-License-Identifier: MIT
#
# Builder-Hex0 is a small bootable machine image which has
# the ability to compile hex0 code. It is also written in hex0
# and so it can build itself if provided with its own source code.
#
# hex0 is a "language" for binary encoding in hexadecimal
# with support for comments.
# 16 bit Functions
# ----------------
# MBR_entry
# console_putc_16
# console_put_hex_16
#
# get_drive_geometry
# next_sector
# read_sectors_16
# write_sectors_16
#
# [GDT data]
# MBR_main
# 32 bit Functions
# ----------------
# setup_interrupt_handlers
# stub_interrupt_handler
# enter_16bit_real
# resume_32bit_mode
#
# console_putc
# console_put_hex
# console_puts
# read_sectors
# write_sectors
#
# syscall_interrupt_handler
# handle_syscall_open
# handle_syscall_close
# absolute_path
# find_file
# fd_to_file_index
# handle_syscall_read
# handle_syscall_brk
# handle_syscall_write
# handle_syscall_fork
# handle_syscall_execve
# handle_syscall_chdir
# handle_syscall_exit
# handle_syscall_waitpid
# handle_syscall_lseek
# handle_syscall_access
# handle_syscall_mkdir
# handle_syscall_getcwd
#
# strcmp
#
# read
# write
# src
# hex0
# internalshell
#------------------------------------------------------------
# Memory:
# 54000000 - BFFFFFFF files (~1812MB)
# 30000000 - 53FFFFFF saved processes (~604MB)
# 08048000 - 2FFFFFFF current running process (~670MB)
# 01080000 - 08000000 32 bit stack (117MB)
# 01000010 - 0107FFFF file descriptors (16 bytes each * 32K)
# { unused, address, length, unused }
# 01000000 - 0100000F stdin disk locator
# { cylinder/sector (2 bytes), head (1 byte), unused byte,
# offset (2 bytes), unused }
# 201800 - FFFFFF file names 6..14335
# 201400 - 2017FF file name 5
# 201000 - 2013FF file name 4
# 100000 - 200FF0 unused
# 9FC00 - FFFFF BIOS
# 40200 - 9FBFF scratch buffer
# 40000 - 401FF stdin device buffer
# 20000 - 3FFFF process descriptors ( 16 * 4096 bytes each)
# offset len description
# 0x000 0x004 process address
# 0x004 0x004 brk pointer
# 0x008 0x004 saved stack pointer (first process only)
# 0x00C 0x004 saved stack pointer
# 0x010 0x004 forked?
# 0x014 0x004 saved brk pointer
# 0x018 0x004 child exit code
# 0x01C 0x004 address of saved process memory
# 0x020 0x004 length of process memory
# 0x024 0x004 address of saved process stack
# 0x028 0x004 length of saved process stack
# 0x02C 0x01C unused
# 0x100 0x100 current directory
# 0x200 x0E00 file descriptors 448 * 8 bytes each
# { global_file_index, current_offset }
# 10800 - 1FFFF unused
# 10000 - 107FF interrupt table
# A000 - A1FF sector read buffer - 16bit
# 7C00 - 8600 code
# 7B00 - 7BFF Saved 32 bit registers while in 16 bit mode
# < 7B00 real mode stack
#------------------------------------------------------------
#------------------------------------------------------------
#[7C00]
#:MBR_entry
# inputs:
# dl: boot drive
#
# We cannot be sure the registers are initialized to zero so we
# do that first. We far jump to MBR_main in order to set CS.
31 C0 # xor ax, ax
8E D8 # mov ds, ax
8E C0 # mov es, ax
8E D0 # mov ss, ax
BC 00 7B # mov sp, 0x7B00
FC # cld ; clear direction flag
EA 26 7D 00 00 # jmp MBR_main
#------------------------
#[7C11]
#:console_putc_16
# input:
# al: char to print
#
# Note: with QEMU+Seabios this does not flush the last character or
# CRLF of a line until the first character is output on the next line
# and that character cannot be another CRLF.
53 # push bx
50 # push ax
# Prepare to use BIOS tty output interrupt.
# Specify text page 00, 0 black background, 7 light grey text
BB 00 07 # mov bx, 0x0007
# Specify the `write character` BIOS routine
B4 0E # mov ah, 0x0E
3C 0A # cmp al, 0x0A
75 06 # jne regular
# convert LF to CR LF for BIOS output
B0 0D # mov al, 0x0D
CD 10 # int 0x10
B0 0A # mov al, 0x0A
#:regular
CD 10 # int 0x10
58 # pop ax
5B # pop bx
CB # retf
#------------------------
#[7C27]
#:console_put_hex_16
# input:
# al: byte to print as hex
#
50 # push ax
24 F0 # and al, 0xF0
C0 E8 04 # shr al, 4
3C 09 # cmp al, 9
7F 04 # jg alpha1
# numeral
04 30 # add al, 0x30
EB 02 # jmp print1
#:alpha1
04 37 # add al, 0x37
#:print1
9A 11 7C 00 00 # call console_putc_16
58 # pop ax ; restore original al
50 # push ax ;
24 0F # and al, 0x0F
3C 09 # cmp al, 9
7F 04 # jg alpha2
# numeral
04 30 # add al, 0x30
EB 02 # jmp print2
#:alpha2
04 37 # add al, 0x37
#:print2
9A 11 7C 00 00 # call console_putc_16
58 # pop ax
CB # retf
#-------------
#[7C51]
#:boot_drive
80
#-------------
#[7C52]
#:max_head
0F
#-------------
#[7C53]
#:max_sector
3F
#------------------------
#[7C54]
#:get_drive_geometry
# input:
# dl: drive
#
06 # push es
57 # push di
# https://en.wikipedia.org/wiki/INT_13H#INT_13h_AH=08h:_Read_Drive_Parameters
31 FF # xor di, di
8E C7 # mov es, di
B4 08 # mov ah, 8 ; get drive parameters
CD 13 # int 0x13
88 36 52 7C # mov [max_head], dh ; max_head
80 E1 3F # and cl, 0x3f
88 0E 53 7C # mov [max_sector], cl ; num_sectors
5F # pop di
07 # pop es
CB # retf
#------------------------
#[7C6C]
#:next_sector
# inputs:
# cx: cylinder/sector
# dh: head
#
50 # PUSH_AX
88 C8 # mov al, cl ; get sector number
24 3F # and al, 0x3f
3A 06 53 7C # cmp al, [max_sector]; if sector_num == max_sector
74 04 # je next_head ; goto next_head
FE C1 # inc cl ; else sector_num++;
EB 28 # jmp next_sector_finish
#:next_head
3A 36 52 7C # cmp dh, [max_head] ; if head_num == max_head
74 09 # je next_cylinder ; goto next_cyclinder
FE C6 # inc dh ; else head_num++
80 E1 C0 # and cl, 0xc0 ; sector_num = 1
FE C1 # inc cl ;
EB 19 # jmp next_sector_finish
#:next_cylinder
80 FD FF # cmp ch, 0xff ; if cylinder_low == 255
74 0B # je next_cyl_high
#:next_cyl_low
30 F6 # xor dh, dh ; head_num = 0
80 E1 C0 # and cl, 0xc0 ; sector_num = 0
81 C1 01 01 # add cx, 0x0101 ; cylinder_low++, sector_num++
EB 09 # jmp next_sector_finish
#:next_cyl_high
30 F6 # xor dh, dh ; head_num = 0
81 E1 C0 00 # and cx, 0x00C0 ; cylinder_low = 0, sector_num = 0
80 C1 41 # add cl, 0x41 ; cylinder_high++, sector_num++
#:next_sector_finish
58 # pop ax
CB # retf
#------------------------
#[7CA5]
#:read_sectors_16
# inputs:
# di: dest_addr
# cx: cylinder/sector
# dh: head
# ax: num_sectors
#
# outputs:
# di: next byte to write to
# cx,dh: next disk sector to read from
#
50 # push ax
53 # push bx
56 # push si
89 C6 # mov si, ax ; si=num_sectors
89 FB # mov bx, di ; int 13 writes to bx
#:read_one_loop
8A 16 51 7C # mov dl, [boot_drive]
B4 02 # mov ah, 2 ; rw mode = 02 (read)
B0 01 # mov al, 1 ; num_sectors
CD 13 # int 0x13
72 F4 # jnc read_one_loop
3C 01 # cmp al, 1
75 F0 # jnz read_one_loop
# advance and maybe continue
9A 6C 7C 00 00 # call next_sector
# we read one sector, advance
81 C3 00 02 # add bx, 0x200
4E # dec si ; num_sectors--
75 E4 # jnz read_one_loop
89 DF # mov di, bx
5E # pop si
5B # pop bx
58 # pop ax
CB # retf
#------------------------
#[7CCE]
#:write_sectors_16
# inputs:
# si: source_addr
# cx: cylinder/sector
# dh: head
# ax: num_sectors
#
# outputs:
# si: next byte to read from
# cx,dh: next disk sector to read from
#
50 # push ax
53 # push bx
57 # push di
89 C7 # mov di, ax ; di=num_sectors
89 F3 # mov bx, si ; int 13 reads from [bx]
#:write_one_loop
8A 16 51 7C # mov dl, [boot_drive]
B4 03 # mov ah, 3 ; rw mode = 03 (write)
B0 01 # mov al, 1 ; num_sectors
CD 13 # int 0x13
72 F4 # jnc write_one_loop
3C 01 # cmp al, 1
75 F0 # jnz write_one_loop
# advance and maybe continue
9A 6C 7C 00 00 # call next_sector
# we write one sector, advance
81 C3 00 02 # add bx, 0x200
4F # dec di ; num_sectors--
75 E4 # jnz write_one_loop
89 DE # mov si, bx
5F # pop di
5B # pop bx
58 # pop ax
CB # retf
#alignment only
00
#---------------------------------------------
# The Global Descriptor Table for 32 bit mode.
#---------------------------------------------
#[7CF8]
#:GDT_start
00 00 00 00 00 00 00 00
#:GDT_code32
FF FF # limit 0:15
00 00 # base 0:15
00 # base 16:23
9A # access byte 10011010b
# present=1 privilege=00 type=1
# code=1 conforming=0 readable=1 accessed=0
CF # 11001111b
# granularity=1 32-bit-default=1 64-bit seg=0 AVL=0
00 #
#:GDT_data32
FF FF # limit 0:15
00 00 # base 0:15
00 # base 16:23
92 # access byte 10010010b
# present=1 privilege=00 type=1
# code=0 conforming=0 readable=1 accessed=0
CF # flags, limit 16:19 11001111b
# granularity=1 32-bit-default=1 64-bit seg=0 AVL=0
00 # base 24:31
#:GDT_code16
FF FF # limit 0:15
00 00 # base 0:15
00 # base 16:23
9A # access byte 10011010b
# present=1 privilege=00 type=1
# code=1 conforming=0 readable=1 accessed=0
8F # 10001111b
# granularity=1 32-bit-default=0 64-bit seg=0 AVL=0
00 #
#:GDT_data16
FF FF # limit 0:15
00 00 # base 0:15
00 # base 16:23
92 # access byte 10010010b
# present=1 privilege=00 type=1
# code=0 conforming=0 readable=1 accessed=0
8F # flags, limit 16:19 10001111b
# granularity=1 32-bit-default=0 64-bit seg=0 AVL=0
00 # base 24:31
#------
#[7D20]
#:GDT_locator
27 00 # length
F8 7C 00 00 # GDT_start
#------------------------
#[7D26]
#:MBR_main
# inputs:
# dl: boot_drive
#
# Load the kernel and jump to it
88 16 51 7C # mov [boot_drive], dl
9A 54 7C 00 00 # call get_drive_geometry()
BF 00 7E # mov di, 0x7E00 ; place remaining code after MBR in memory
B8 07 00 # mov ax, 0x0007 ; num_sectors = 7
B9 02 00 # mov cx, 0x0002 ; cylinder = 0, sector_num = 0x02
B6 00 # mov dh, 0 ; head = 0
9A A5 7C 00 00 # call read_sectors_16
# start 32bit mode
B8 01 24 # mov ax,2401h # enable A20 line
CD 15 # int 15h
FA # cli
0F 01 16 20 7D # lgdt GDT_locator
0F 20 C0 # mov eax, cr0
66 83 C8 01 # or eax, 0x01
0F 22 C0 # mov cr0, eax
EA 59 7D 08 00 # jmp setup_32bit ; sets CS
#------
#[7D59]
#:setup_32bit
66 B8 10 00 # mov ax, 0x0010 ; data descriptor
8E D8 # mov ds, ax
8E D0 # mov ss, ax
8E C0 # mov es, ax
8E E0 # mov fs, ax
8E E8 # mov gs, ax
BD 00 00 00 08 # mov ebp, 0x08000000
89 EC # mov esp, ebp
9A 7C 7D 00 00 08 00 # call setup_interrupt_handlers
EA A1 88 00 00 08 00 # jmp internalshell
#----------------------------------------
#[7D7C]
#:setup_interrupt_handlers
53 # push ebx
# handle the timer interrupt 08
BB 40 00 01 00 # mov ebx, &interrupt_table[08]
66 C7 03 BC 7D # mov word [ebx + 0], low_address stub_interrupt_handler
66 C7 43 06 00 00 # mov word [ebx + 6], high_address
66 C7 43 02 08 00 # mov word [ebx + 2], code_segment = 0x0800
C6 43 05 8E # mov byte [ebx + 5], flags = 8E
# handle int 80
BB 00 04 01 00 # mov ebx, &interrupt_table[80]
66 C7 03 D8 7E # mov word [ebx + 0], low_address syscall_interrupt_handler
66 C7 43 06 00 00 # mov word [ebx + 6], high_address
66 C7 43 02 08 00 # mov word [ebx + 2], code_segment = 0x0800
C6 43 05 8E # mov byte [ebx + 5], flags = 8E
# load the interrupt table
FA # cli
0F 01 1D BD 7D 00 00 # lidt IDT_locator_32
FB # sti
5B # pop ebx
CB # retf
#----------------------------------------
#[7DBC]
#:stub_interrupt_handler
CF # iret
#----------------------------------------
#[7DBD]
#:IDT_locator_32
FF 07 # length
00 00 01 00 # IDT_start
# unused
00 00
#------------------------------------------------------------
# 32 -> 16 -> 32 bit switching functions
#------------------------------------------------------------
# When switching between real mode and
# protected, registers are stored here:
#
# 7B14 edx
# 7B10
# 7B0C
# 7B08 eax
# 7B04 esp
#
# 7B00 <- top of real mode stack
#----------------------------------------
#[7DC5]
#:enter_16bit_real
FA # cli
A3 08 7B 00 00 # mov [0x7B08], eax ; preserve so we can use these locally
89 15 14 7B 00 00 # mov [0x7B14], edx ;
5A # pop edx ; capture return address
89 25 04 7B 00 00 # mov [0x7B04], esp ; capture stack
# The following far jump sets CS to a 16-bit protected mode selector
# and the segment registers are also set to 16-bit protected mode selectors.
# This is done prior to entering real mode.
EA DF 7D 00 00 18 00 # jmp 0x18:setup_16bit
#------
#[7DDF]
#:setup_16bit
B8 20 00 # mov eax, 0x0020
8E D0 # mov ss, eax
8E D8 # mov ds, eax
8E C0 # mov es, eax
8E E8 # mov gs, eax
8E E0 # mov fs, eax
BC 00 7B # mov sp, 0x7B00
0F 20 C0 # mov eax, cr0
66 83 E0 FE # and eax, 0xfffffffe ; clear protected mode
0F 22 C0 # mov cr0, eax
# The following far jump sets CS to a 16-bit real mode segment
# and the segment registers are also set to real mode segments.
EA 00 7E 00 00 # jmp 0000:XXXX real_mode
# [7DFE]
# This is the DOS/MBR identifier at offset 510:
55 AA
#------
#[7E00]
#:real_mode
B8 00 00 # mov ax, 0x0
8E D8 # mov ds, ax
8E E0 # mov fs, ax
8E E8 # mov gs, ax
8E D0 # mov ss, ax
8E C0 # mov es, ax
BC 00 7B # mov sp, 0x7B00
FA # cli
0F 01 1E 22 7E # lidt IDT_locator_16
FB # sti
# Using retf to set CS comes from here:
# https://stackoverflow.com/questions/26448480/bios-interrupts-in-protected-mode
# This page recommends a far jump followed by sti:
# https://www.sudleyplace.com/pmtorm.html
6A 00 # push 0x0000 (2 bytes!) CS to return to
52 # push dx IP to return to
A1 08 7B # mov ax, [0x7B08] ; restore from above
8B 16 14 7b # mov dx, [0x7B14]
CB # retf
#------
#[7E22]
#:IDT_locator_16
FF FF
00 00 00 00
#----------------------------------------
#[7E28]
#:resume_32bit_mode
FA # cli
A3 08 7B # mov [0x7B08], ax ; preserve, they might be return values from 16 bit
89 16 14 7b # mov [0x7B14], dx
5A # pop dx ; carry the return IP in dx
58 # pop ax ; CS
0F 01 16 20 7D # lgdt GDT_locator
0F 20 C0 # mov eax, cr0
66 83 C8 01 # or eax, 0x01 ; enable protected mode
0F 22 C0 # mov cr0, eax
EA 46 7E 08 00 # jmp restore_32bit
#------
#[7E46]
#:restore_32bit
B8 10 00 00 00 # mov eax, 0x0010 ; data descriptor
8E D8 # mov ds, eax
8E D0 # mov ss, eax
8E C0 # mov es, eax
8E E0 # mov fs, eax
8E E8 # mov gs, eax
8B 25 04 7B 00 00 # mov esp, [0x7B04] ; restore, (saved in enter_16bit_mode)
9A 7C 7D 00 00 08 00 # call setup_interrupt_handlers
52 # push edx ; setup our return location
# These restore the 16 bit portion of these registers, which may be a
# return value from a 16 bit function, and they also restore any previous high
# bits that were stored by enter_16bit_mode so these registers need not be
# saved when going to 16 bit mode and back if you want them left alone.
A1 08 7B 00 00 # mov eax, [0x7B08] ; restore, (saved at top of this function)
8B 15 14 7B 00 00 # mov edx, [0x7B14]
C3 # ret
#------------------------
#[7E6F]
#:console_putc
#
E8 51 FF FF FF # CALL enter_16bit_real, next=[7E74]
9A 11 7C 00 00 # CALL console_putc_16(al)
9A 28 7E 00 00 # CALL resume_32bit_mode
CB # RETF
#------------------------
#[7E7F]
#:console_put_hex
E8 41 FF FF FF # CALL enter_16bit_real, next=[7E84]
9A 27 7C 00 00 # CALL console_put_hex_16(al)
9A 28 7E 00 00 # CALL resume_32bit_mode
CB # RETF
#------------------------
#[7E8F]
#:console_puts
# inputs
# ds:si: string to print
#:puts_loop
50 # push eax
56 # push esi
8A 06 # mov al, [esi]
3C 00 # cmp al, 0
74 0A # jz end_puts_loop
9A 6F 7E 00 00 08 00 # call console_putc
46 # inc esi
EB F0 # jmp puts_loop
#:end_puts_loop
B0 0A # mov al, 0A
9A 6F 7E 00 00 08 00 # call console_putc
5E # pop esi
58 # pop eax
CB # RETF
#------------------------
#[7EAD]
#:read_sectors
# inputs:
# di: dest_addr
# cx: cylinder/sector
# dh: head
# ax: num_sectors
#
E8 13 FF FF FF # CALL enter_16bit_real, next=[7EB2]
9A A5 7C 00 00 # CALL read_sectors_16
9A 28 7E 00 00 # CALL resume_32bit_mode
CB # RETF
#------------------------
#[7EBD]
#:write_sectors
# inputs:
# si: source_addr
# cx: cylinder/sector
# dh: head
# ax: num_sectors
#
E8 03 FF FF FF # CALL enter_16bit_real, next=[7EC2]
9A CE 7C 00 00 # CALL write_sectors_16
9A 28 7E 00 00 # CALL resume_32bit_mode
CB # RETF
#------------------------
#[7ECD]
#:reboot
E8 F3 FE FF FF # call enter_16bit_real, next=[7ED2]
FA # cli
EA F0 FF 00 F0 # ljmp $F000:FFF0 ; reboot
#------------------------
#[7ED8]
#:syscall_interrupt_handler
#
3C 01 # cmp al, 1
75 08 # jne try next
9A B7 84 00 00 08 00 # call handle_syscall_exit
CF # iret
3C 02 # cmp al, 2
75 08 # jne try next
9A B0 82 00 00 08 00 # call handle_syscall_fork
CF # iret
3C 03 # cmp al, 3
75 08 # jne try next
9A 5B 81 00 00 08 00 # call handle_syscall_read
CF # iret
3C 04 # cmp al, 4
75 08 # jne try next
9A 3D 82 00 00 08 00 # call handle_syscall_write
CF # iret
3C 05 # cmp al, 5
75 08 # jne try next
9A 94 7F 00 00 08 00 # call handle_syscall_open
CF # iret
3C 06 # cmp al, 6
75 08 # jne try next
9A 5E 80 00 00 08 00 # call handle_syscall_close
CF # iret
3C 07 # cmp eax, 7
75 08 # jne try next
9A 0B 85 00 00 08 00 # call handle_syscall_waitpid
CF # iret
3C 0B # cmp eax, B
75 08 # jne try next
9A 1E 83 00 00 08 00 # call handle_syscall_execve
CF # iret
3C 0C # cmp al, C
75 08 # jne try next
9A 60 84 00 00 08 00 # call handle_syscall_chdir
CF # iret
3C 13 # cmp al, 0x13
75 08 # jne try next
9A 25 85 00 00 08 00 # call handle_syscall_lseek
CF # iret
3C 2D # cmp al, 2D
75 08 # jne try next
9A 0E 82 00 00 08 00 # call handle_syscall_brk
CF # iret
3C 21 # cmp al, 0x21
75 08 # jne try next
9A 6D 85 00 00 08 00 # call handle_syscall_access
CF # iret
3C 27 # cmp al, 0x27
75 08 # jne syscall_ok
9A 83 85 00 00 08 00 # call handle_syscall_mkdir
CF # iret
# wait4
3C 72 # cmp eax, 0x72
75 05 # jne try next
31 C0 # xor eax, eax
89 01 # mov [ecx], eax
CF # iret
3C B7 # cmp al, 0xB7
75 08 # jne syscall_ok
9A 99 85 00 00 08 00 # call handle_syscall_getcwd
CF # iret
#:syscall_ok
# return success for all unimplemented syscalls
31 C0 # xor eax, eax
CF # iret
#------
#[7F8C]
#:next_filenum
04 00 00 00
#------
#[7F90]
#:next_file_address
00 00 00 54
#----------------------------------------
#[7F94]
#:handle_syscall_open
# inputs:
# ebx: filename
# ecx: flags
#
53 # push ebx
51 # push ecx
56 # push esi
57 # push edi
9A 76 80 00 00 08 00 # call absolute_path
F7 C1 40 00 00 00 # test ecx, 0x40 ; 0x40 is O_CREAT
74 72 # jz open_read
# Create new file
# Exit with error if directory does not exist
# Go the end of filename
89 DE # mov esi, ebx
#:end_loop
AC # lodsb
3C 00 # cmp al, 0
75 FB # jne end_loop
# Look in reverse for last slash
4E # dec esi
FD # std ; go backwards
#:find_slash_loop
AC # lodsb
3C 2F # cmp al, '/'
75 FB # jne find_slash_loop
# If first slash at start, its root, so it exists
FC # cld
46 # inc esi
39 DE # cmp esi, ebx
74 1C # je after_dir_lookup
# Zero out slash
31 C0 # xor eax, eax
89 F7 # mov edi, esi
AA # stosb
# Lookup directory
9A 13 81 00 00 08 00 # call find_file ; eax=find_file(ebx)
# Restore first char
C6 47 FF 2F # mov byte [edi - 1], 0x2f
83 F8 FF # cmp eax, -1
75 07 # jne after_dir_lookup
EA 59 80 00 00 08 00 # jmp syscall_open_finish_fail
#:after_dir_lookup
# copy filename to new slot
89 DE # mov esi, ebx
BF 00 00 20 00 # mov edi, 0x0200000
A1 8C 7F 00 00 # mov eax, [&next_filenum]
C1 E0 0A # shl eax, 0a
01 C7 # add edi, eax
B9 00 04 00 00 # mov ecx, 0x0000400
F3 A4 # rep movsb
# set address of file
BF 00 00 00 01 # mov edi, 0x01000000 ; pfile_descriptor = &file_descriptor[0]
A1 8C 7F 00 00 # mov eax, [&next_filenum]
C1 E0 04 # shl eax, 04
01 C7 # add edi, eax ; pfile_descriptor += sizeof(file_descriptor) * next_filenum
8B 0D 90 7F 00 00 # mov ecx, [next_file_address]
89 4F 04 # mov [edi+4], ecx ; pfile_descriptor->file_addr = ecx
31 C0 # xor eax, eax
89 47 08 # mov [edi+8], eax ; pfile_descriptor->length = 0
A1 8C 7F 00 00 # mov eax, [next_filenum] ; return next_filenum
FF 05 8C 7F 00 00 # inc [next_filenum]
EB 18 # jmp syscall_open_finish
#open_read
9A 13 81 00 00 08 00 # call find_file
83 F8 FF # cmp eax, -1
74 34 # je syscall_open_finish_fail
89 C1 # mov ecx, eax
# set read offset to start of file
BE 00 00 00 01 # mov esi, 0x01000000 ; pfile_descriptor = &file_descriptor[0]
C1 E1 04 # shl ecx, 04
01 CE # add esi, ecx ; pfile_descriptor += sizeof(file_descriptor) * filenum
#:syscall_open_finish
8B 35 1A 83 00 00 # mov esi, [&next_process_num]
4E # dec esi = current process
C1 E6 0C # shl esi, 0x0C
81 C6 20 02 02 00 # add esi, 0x0020220 ; pproc_descriptor = &pproc_descriptor[current_process_num].open_files[4]
6A 04 # push 0x04 ; start at fd=4
59 # pop ecx
#:find_slot_loop
8B 1E # mov ebx, [esi] ; get file number of fd slot
85 DB # test ebx, ebx ; is this fd slot available?
74 06 # jz got_slot
41 # inc ecx ; no, go to next slot
83 C6 08 # add esi, 0x08
EB F4 # jmp find_slot_loop
#:got_slot
89 06 # mov [esi], eax ; store file number in slot
89 C8 # mov eax, ecx ; return fd
31 C9 # xor ecx, ecx ; set current file offset to zero
89 4E 04 # mov [esi+0x4], ecx
#--------
#[8059]
#:syscall_open_finish_fail
5F # pop edi
5E # pop esi
59 # pop ecx
5B # pop ebx
CB # ret
#----------------------------------------
#[805E]
#:handle_syscall_close
# inputs:
# ebx: fd
57 # push edi
8B 3D 1A 83 00 00 # mov edi, [&next_process_num]
4F # dec edi = current process
C1 E7 0C # shl edi, 0x0C
81 C7 00 02 02 00 # add edi, 0x00020200 ; edi = all_procs[current_process_num].open_files
31 C0 # xor eax, eax
89 04 DF # mov [edi+ebx*8], eax ; open_files[fd].global_index = 0
5F # pop edi
CB # ret
#----------------------------------------
#[8076]
#:absolute_path
# inputs:
# ebx: path
# outputs:
# ebx: absolute path
#
50 # push eax
52 # push edx
56 # push esi
57 # push edi
BF 00 02 04 00 # mov edi, 0x00040200 ; scratch buffer
57 # push edi
# if absolute path, skip prefixing current directory
80 3b 2f # cmp [ebx], '/'
74 18 # je strcpy_path_arg
# get cwd
8B 35 1A 83 00 00 # mov esi, [&next_process_num]
4E # dec esi = current process
C1 E6 0C # shl esi, 0x0C
81 C6 00 01 02 00 # add esi, 0x0020100 ; pproc_descriptor = &pproc_descriptor[current_process_num].current_dir
#:strcpy_cwd_loop
AC # lodsb
84 C0 # test al, al
74 03 # jz strcpy_path_arg
AA # stosb
EB F8 # jmp strcpy_cwd_loop
#:strcpy_path_arg
89 DE # mov esi, ebx
# skip leading ./
66 81 3E 2E 2F # cmp word [esi], 0x2F2E
75 02 # jne strcpy_path
46 # inc esi
46 # inc esi
#:strcpy_path
31 DB # xor ebx, ebx ; init last_char
#:strcpy_path_loop
AC # lodsb
3C 2F # cmp al, '/'
75 05 # jne ok_path_char
80 FB 2F # cmp bl, '/'
74 01 # je skip_extra_slash
#:ok_path_char
AA # stosb
#:skip_extra_slash
84 C0 # test al, al
74 04 # jz maybe_strip_ending_slash
88 C3 # mov bl, al ; save last_char
EB ED # jmp strcpy_path_loop
#:maybe_strip_ending_slash
80 FB 2F # cmp bl, '/'
75 05 # jne handle_dots
31 C0 # xor eax, eax
4F # dec edi
4F # dec edi
AA # stosb
# handle /. and /..
#:handle_dots
5A # pop edx ; record first parent
52 # push edx
#:handle_dots_loop
5E # pop esi ; get start location
56 # push esi ; save start location
## find_slash
AC # lodsb
3C 00 # cmp al, 0
74 33 # je absolute_path_finish
3C 2F # cmp al, '/'
75 F7 # jne find_slash
#:found_slash
AC # lodsb
# check for /. or /..
3C 00 # cmp al, 0
74 2A # je absolute_path_finish
3C 2E # cmp al, '.'
74 06 # je dot_or_dotdot
89 F2 # mov edx, esi ; record start of parent
4A # dec edx ; go back to slash
4A # dec edx
EB E8 # jmp find_slash
#:dot_or_dotdot
AC # lodsb
3C 2E # cmp al, '.'
75 0A # jne remove_slashdot
#:remove_parent
89 D7 # mov edi, edx
AC # lodsb
AA # stosb
3C 00 # cmp al, 0
75 FA # jne copy
EB D7 # jmp handle_dots_loop
#:remove_slashdot
3C 00 # cmp al, 0
75 01 # jne not_ending_slashdot
4E # dec esi ; go back to null
#:not_ending_slashdot
89 F7 # mov edi, esi
4F # dec edi
4F # dec edi
#:copy_over_slashdot
AC # lodsb
AA # stosb
3C 00 # cmp al, 0
75 FA # jne copy_over_slashdot
EB C6 # jmp handle_dots_loop
#:absolute_path_finish
5B # pop ebx
# restore / if necessary
80 3B 00 # cmp byte [ebx], 0
75 05 # jne abs_path_done
66 C7 03 2F 00 # mov word [ebx], 0x002F
#:abs_path_done
5F # pop edi
5E # pop esi
5A # pop edx
58 # pop eax
CB
#----------------------------------------
#[8113]
#:find_file
# inputs:
# ebx: file_name
# outputs:
# eax: filenum
#
51 # push ecx
52 # push edx
56 # push esi
57 # push edi
A1 8C 7F 00 00 # mov eax, [next_filenum]
48 # dec eax
89 DE # mov esi, ebx
#:checkfile
83 F8 03 # cmp eax, 3
74 17 # je not_found
89 C7 # mov edi, eax
C1 E7 0A # shl edi, 0x0a
81 C7 00 00 20 00 # add edi, 0x0200000
9A C6 85 00 00 08 00 # call strcmp
74 08 # je find_file_finish
48 # dec eax
EB E4 # jmp checkfile
#:not_found
B8 FF FF FF FF # mov eax, 0xffffffff
#:find_file_finish
5F # pop edi
5E # pop esi
5A # pop edx
59 # pop ecx
CB # ret
#------------------------------------------------------------
#[8145]
#:fd_to_file_index
# inputs:
# ebx: file descriptor number
# outputs:
# ebx: global file index
57 # push edi
8B 3D 1A 83 00 00 # mov edi, [&next_process_num]
4F # dec edi = current process
C1 E7 0C # shl edi, 0x0C
81 C7 00 02 02 00 # add edi, 0x00020200 ; edi = all_procs[current_process_num].open_files
8B 1C DF # mov ebx, [edi+ebx*8]
5F # pop edi
CB # ret
#------------------------------------------------------------
#[815B]
#:handle_syscall_read
# inputs:
# ecx: *return_char
# ebx: file
# edx: length
#
53 # push ebx
51 # push ecx
52 # push edx
56 # push esi
57 # push edi
51 # push ecx ; we need this later to return char
83 FB 00 # cmp ebx, 0
75 5F # jne read_memfile
# stdin disk position is stored in fd 0
# get current position
BB 00 00 00 01 # mov ebx, 0x01000000
66 8B 0B # mov cx, [ebx]
8A 73 02 # mov dh, [ebx+2]
31 C0 # xor eax, eax
66 8B 43 04 # mov ax, [ebx+4]
#end of sector?
66 3D ff 01 # cmp ax, 0x01ff
74 04 # je read_next_sector
#:nextchar
66 40 # inc ax
EB 2A # jmp getchar
#:read_next_sector
BF 00 A0 00 00 # mov edi, 0x000A000
B8 01 00 00 00 # mov eax, 0x0001 ; num_sectors = 1
9A AD 7E 00 00 08 00 # call read_sectors
# save new location and offset
66 89 0b # mov [ebx], cx
88 73 02 # mov [ebx+2], dh
31 C0 # xor eax, eax
# move block to device buffer
BE 00 A0 00 00 # mov esi, 0x000A000
BF 00 00 04 00 # mov edi, 0x0040000
B9 00 02 00 00 # mov ecx, 0x0000200
F3 A4 # rep movsb
#:getchar
66 A3 04 00 00 01 # mov [0x01000004], ax
59 # pop ecx
BB 00 00 04 00 # mov ebx, 0x40000 ; device buffer
89 C6 # mov esi, eax ; offset
8A 04 33 # mov al, [ebx+esi+0]
88 01 # mov [ecx], al
B8 01 00 00 00 # mov eax, 1
EB 43 # jmp syscall_read_finish
#:read_memfile
89 D8 # mov eax, ebx ; eax = fd
9A 45 81 00 00 08 00 # call fd_to_file_index ; ebx = global file index
# get pointer to global file
BE 00 00 00 01 # mov esi, 0x01000000 ; pfile_descriptor = &file_descriptor[0]
C1 E3 04 # shl ebx, 04
01 DE # add esi, ebx ; pfile_descriptor += sizeof(file_descriptor) * filenum
# prepare to read
5F # pop edi ; edi = p_dst
8B 5E 04 # mov ebx, [esi+4] ; ebx = pfile_descriptor->file_address
89 D9 # mov ecx, ebx ;
03 4E 08 # add ecx, [esi+0x08] ; ecx = file_address + length
49 # dec ecx ; ecx = last address to read
8B 35 1A 83 00 00 # mov esi, [&next_process_num]
4E # dec esi = current process
C1 E6 0C # shl esi, 0x0C
81 C6 04 02 02 00 # add esi, 0x0020204
C1 E0 03 # shl eax, 3
01 C6 # add esi, eax ; esi = &all_procs[current_proc_num].files[eax].current_offset
03 1E # add ebx, [esi] ; ebx = file_addr + current_offset
87 F3 # xchg esi, ebx ; esi = p_src, ebx = &pproc_descriptor->offset
31 C0 # xor eax, eax ; bytes_read = 0
#:syscall_read_loop
39 CE # cmp esi, ecx ; past the end?
77 07 # ja syscall_read_finish
A4 # movsb
40 # inc eax ; bytes_read++
FF 03 # inc long [ebx] ; (*pcurrent_offset)++
4A # dec edx ; length_to_read--
75 F5 # jnz syscall_read_loop
#:syscall_read_finish
5F # pop edi
5E # pop esi
5A # pop edx
59 # pop ecx
5B # pop ebx
CB # ret
#------------------------------------------------------------
#[820E]
#:handle_syscall_brk
56 # push esi
A1 1A 83 00 00 # mov eax, [&next_process_num]
48 # dec eax = current process
BE 00 00 02 00 # mov esi, 0x0020000 ; pproc_descriptor = &proc_descriptor[0]
C1 E0 0C # shl eax, 0x0C
01 C6 # add esi, eax ; pproc_descriptor += sizeof(proc_descriptor) * procnum
85 DB # test ebx, ebx ; if ebx == 0, just return the current brk
74 15 # jz get_brk
# set
# initialize memory to zero
57 # push edi
8B 7E 04 # mov edi, [esi+4]
31 C0 # xor eax, eax
#:init_loop
39 DF # cmp edi, ebx
74 03 # je init_done
AA # stosb
EB F9 # jmp init_loop
#:init_done
5F # pop edi
89 5E 04 # mov [esi+4], ebx
89 D8 # mov eax, ebx
5E # pop esi
CB # ret
#:get_brk
8B 46 04 # mov eax, [esi+4] ; pproc_descriptor->brk
5E # pop esi
CB # ret
#------------------------------------------------------------
#[823D]
#:handle_syscall_write
# inputs:
# ebx: file
# ecx: address of char to write
# edx: num bytes to write
51 # push ecx
52 # push edx
56 # push esi
57 # push edi
31 C0 # xor eax, eax ; bytes_written = 0
83 FB 02 # cmp ebx, 02 ; std file?
7F 14 # jg write_memfile
# stdout,stderr -> console_out
#:std_loop
85 D2 # test edx, edx
74 5B # jz syscall_write_finish
50 # push eax ; save num_written
8A 01 # mov al, [ecx]
9A 6F 7E 00 00 08 00 # call console_putc
58 # pop eax ; restore num_written
40 # inc eax ; num_written++
41 # inc ecx ; p_dst++
4A # dec edx ; count--
EB EC # jmp std_loop
#:write_memfile
89 CE # mov esi, ecx
# use ecx as pointer to fd current offset
8B 0D 1A 83 00 00 # mov ecx, [&next_process_num]
49 # dec ecx = current process
C1 E1 0C # shl ecx, 0x0C
81 C1 04 02 02 00 # add ecx, 0x0020204
53 # push ebx
C1 E3 03 # shl ebx, 3
01 D9 # add ecx, ebx ; ecx = &all_procs[current_proc_num].files[ebx].current_offset
5B # pop ebx
# lookup global file index from file descriptor
9A 45 81 00 00 08 00 # call fd_to_file_index
C1 E3 04 # shl ebx, 04
81 C3 00 00 00 01 # add ebx, 0x01000000 ; pfile_descriptor += sizeof(file_descriptor) * filenum
8B 7B 04 # mov edi, [ebx+4] ; edi = pfile_descriptor->file_address
03 39 # add edi, [ecx] ; edi = file_addr + current_offset
#:write_loop
85 D2 # test edx, edx
74 19 # jz syscall_write_finish
A4 # movsb
FF 01 # inc long [ecx] ; current_offset++
# If current offset is past previous file length, then increase length
50 # push eax
8B 01 # mov eax, [ecx]
3B 43 08 # cmp eax, [ebx+0x8]
7E 09 # jle skip_lengthen
FF 43 08 # inc long [ebx+0x8] ; file_length++
FF 05 90 7F 00 00 # inc long [next_file_address]
#:skip_lengthen
58 # pop eax
40 # inc eax ; num_written++
4A # dec edx
EB E3 # jmp write_loop
#:syscall_write_finish
5F # pop edi
5E # pop esi
5A # pop edx
59 # pop ecx
CB # ret
#------
#[82AC]
#:next_save_process_address
00 00 00 30
#----------------------------------------
#[82B0]
#:handle_syscall_fork
53 # push ebx
51 # push ecx
52 # push edx
56 # push esi
57 # push edi
55 # push ebp
A1 1A 83 00 00 # mov eax, [&next_process_num]
48 # dec eax = current process
89 C2 # mov edx, eax
BF 00 00 02 00 # mov edi, 0x0020000 ; pproc_descriptor = &proc_descriptor[0]
C1 E0 0C # shl eax, 0x0C
01 C7 # add edi, eax ; pproc_descriptor += sizeof(proc_descriptor) * procnum
8b 77 04 # mov esi, [edi+0x4] ; save brk pointer
89 77 14 # mov [edi+0x14], esi
89 E6 # mov esi, esp
89 77 0c # mov [edi+0xC], esi ; save stack pointer so we can return again later
FF 47 10 # inc [edi+0x10] ; fork = true
A1 AC 82 00 00 # mov eax, [next_save_process_address] ; set save stack location
89 47 24 # mov [edi+0x24], eax
B9 00 00 00 08 # mov ecx, 0x08000000
29 F1 # sub ecx, esi ; compute save stack length
01 0D AC 82 00 00 # add [next_save_process_address], ecx
89 4F 28 # mov [edi+0x28], ecx
89 C7 # mov edi, eax
F3 A4 # rep movsb ; save stack
# copy current process image to storage
89 D0 # mov eax, edx ; restore current process num
C1 E0 0C # shl eax, 0x0C
05 00 00 02 00 # add eax, 0x0020000
8B 30 # mov esi, [eax] ; esi = pproc_descriptor->process_address
8B 48 14 # mov ecx, [eax+0x14] ; process_length = brk - process_address
29 F1 # sub ecx, esi
89 78 1C # mov [eax+0x1C], edi ; save address of saved process memory
89 48 20 # mov [eax+0x20], ecx ; save length of process memory
01 0D AC 82 00 00 # add [next_save_process_address], ecx
F3 A4 # rep movsb ; copy current process image to storage
31 C0 # xor eax, eax ; return as child, we'll return again as parent when child exits
5D # pop ebp
5F # pop edi
5E # pop esi
5A # pop edx
59 # pop ecx
5B # pop ebx
CB # ret
#------
#[831A]
#:next_process_num
01 00 00 00
#----------------------------------------
#[831E]
#:handle_syscall_execve
# inputs:
# ebx: program_name
# ecx: char **args
# edx: env
#
A1 1A 83 00 00 # mov eax, [next_process_num]
3C 01 # cmp al, 1
75 0A # jne not_first_process
# first process
BD 00 10 02 00 # mov ebp, 0x00021000 ; ebp = &proc_descriptor[1]
89 65 08 # mov [ebp+0x8], esp ; save original stack pointer before pushing args
EB 23 # jmp prepare_stack
# not_first_process
# check if current process forked or not.
# if so, create new process, if not overlay current
48 # dec eax ; eax = current_process
C1 E0 0C # shl eax, 0x0C
05 00 00 02 00 # add eax, 0x0020000 ; pproc_descriptor = &proc_descriptor[0]
8B 68 10 # mov ebp, [eax+0x10] ; create_new_process = pproc->forked
85 ED # test ebp, ebp ; did current process fork?
75 0B # jnz forked
#not_forked
8B 60 0C # mov esp, [eax+0xC] ; no fork so reset initial stack to same as current process
A1 1A 83 00 00 # mov eax, [next_process_num]
48 # dec eax
EB 08 # jump prepare_stack
#:forked
FF 48 10 # dec [eax+0x10] ; fork handled so reset: fork = false
A1 1A 83 00 00 # mov eax, [next_process_num]
#:prepare_stack
# eax=process number to use
# --- env ---
8B 3D AC 82 00 00 # mov edi, [next_save_process_address]
6A 00 # push 0 ; push end of env
#:push_env_loop
# copy env arg to memory for this process
8B 32 # mov esi, [edx]
85 F6 # test esi, esi
74 0F # jz end_env_loop
57 # push edi ; push p_arg
51 # push ecx
B9 00 01 00 00 # mov ecx, 0x00000100 ; memory per arg
F3 A4 # rep movsb ; copy to new memory
59 # pop ecx
83 C2 04 # add edx, 4
EB EB # jmp push_env_loop
#:end_env_loop
# --- args ---
6A 00 # push 0 ; push end of args
# count args
31 C0 # xor eax, eax ; passed_args = 0
#:countloop
83 39 00 # cmp long [ecx], 0
74 06 # jz push_args
40 # inc eax
83 C1 04 # add ecx, 4
EB F5 # jmp countloop
# push_args
89 C2 # mov edx, eax ; save eax (can't push)
#:push_args_loop
83 E9 04 # sub ecx, 4
# copy arg to memory for this process
8B 31 # mov esi, [ecx]
57 # push edi ; push p_arg
51 # push ecx
B9 00 01 00 00 # mov ecx, 0x00000100 ; memory per arg
F3 A4 # rep movsb
59 # pop ecx
48 # dec eax
75 EE # jnz push_args_loop
# finish with argc
89 D0 # mov eax, edx ; restore eax
50 # push eax = argc
# get current process descriptor
A1 1A 83 00 00 # mov eax, [next_process_num]
48 # dec eax
50 # push eax ; save current process num
C1 E0 0C # shl eax, 0x0C
05 00 00 02 00 # add eax, 0x0020000 ; pproc_descriptor = &proc_descriptor[current_process_num]
89 3D AC 82 00 00 # mov [next_save_process_address], edi
# copy cwd from current process
05 00 01 00 00 # add eax, 0x100
89 C6 # mov esi, eax
05 00 10 00 00 # add eax, 0x1000
89 C7 # mov edi, eax
#loop
AC # lodsb
AA # stosb
3C 00 # cmp al, 0
75 FA # jne loop
58 # pop eax ; restore current process num
40 # inc eax ; eax = new process id
# prepare process image in memory
50 # push eax ; save new process id
# get file address and length
9A 76 80 00 00 08 00 # call absolute_path
9A 13 81 00 00 08 00 # call find_file ; eax=find_file(ebx)
# zero process memory.
# Do this after looking up file name because that may come from process memory.
50 # push eax
57 # push edi
31 C0 # xor eax, eax
BF 00 80 04 08 # mov edi, 0x08048000
B9 00 80 FB 26 # mov ecx, 0x26FB8000
F3 AA # rep stosb
5F # pop edi
58 # pop eax
C1 E0 04 # shl eax, 04 ; pfile_descriptor = sizeof(file_descriptor) * filenum
05 00 00 00 01 # add eax, 0x01000000 ; pfile_descriptor += &file_descriptors[0]
8B 40 04 # mov eax, [eax + 0x4] ; eax = pfile_descriptor->file_address
89 C3 # mov ebx, eax ; save file address
31 C9 # xor ecx, ecx
66 8B 48 2C # mov cx, [eax + 0x2C] ; get number of program headers
8B 50 18 # mov edx, [eax + 0x18] ; get process entry address
03 40 1C # add eax, [eax + 0x1C] ; calc first program header address
#:program_header_loop
51 # push ecx ; save program header count
8B 70 04 # mov esi, [eax + 4] ; get segment file source offset
01 DE # add esi, ebx ; calc segment file address
8B 78 08 # mov edi, [eax + 8] ; get segment memory destination address
8B 48 10 # mov ecx, [eax + 0x10] ; get segment length
F3 A4 # rep movsb
83 C0 20 # add eax, 0x20 ; go to next program header
59 # pop ecx ; restore program header count
49 # dec ecx
75 EB # jnz program_header_loop
58 # pop eax ; restore new process num
85 ED # test ebp, ebp ; new process (vs overlay)?
75 01 # jnz record_process_address
48 # dec eax ; overlay
#:record_process_address
C1 E0 0C # shl eax, 0x0C
05 00 00 02 00 # add eax, 0x0020000 ; pproc_descriptor = &pproc_descriptor[current_process_num]
03 5B 1C # add ebx, [ebx + 0x1C] ; calc first program header address
8B 5B 08 # mov ebx, [ebx + 0x8] ; get first segment memory address
89 18 # mov [eax], ebx ; pproc_descriptor->process_address = first segment address
# setup brk
81 C7 00 00 02 00 # add edi, 0x00020000 ; brk after last segment plus 0x20000
89 78 04 # mov [eax + 4], edi ; pproc_descriptor->brk
31 FF # xor edi, edi
89 78 10 # mov [eax + 0x10], edi ; pproc->forked = false
# clear open file descriptors
89 C7 # mov edi, eax
81 C7 00 02 00 00 # add edi, 0x0000200
31 C0 # xor eax, eax
B9 00 0E 00 00 # mov ecx, 0x00000E00
F3 AA # rep stosb
85 ED # test ebp, ebp ; new process (vs overlay)?
74 06 # jz after_new_process
# prepare for next process
FF 05 1A 83 00 00 # inc [next_process_num]
#:after_new_process
# get entry point and jump
52 # push edx
31 C0 # xor eax, eax
31 DB # xor ebx, ebx
31 C9 # xor ecx, ecx
31 D2 # xor edx, edx
31 F6 # xor esi, esi
31 FF # xor edi, edi
31 ED # xor ebp, ebp
C3 # ret
#----------------------------------------
#[8460]
#:handle_syscall_chdir
56 # push esi
57 # push edi
9A 76 80 00 00 08 00 # call absolute_path
9A 13 81 00 00 08 00 # call find_file
83 F8 FF # cmp eax, -1
74 3F # je chdir_finish
C1 E0 04 # shl eax, 04
05 08 00 00 01 # add eax, 0x01000008 ; eax = &file_descriptor[filenum].file_length
83 38 00 # cmp long [eax], 0
74 07 # je chdir_ok
# can't chdir to a file
B8 FF FF FF FF # mov eax, -1
EB 2B # jmp chdir_finish
#:chdir_ok
89 DE # mov esi, ebx
8B 3D 1A 83 00 00 # mov edi, [&next_process_num]
4F # dec edi = current process
C1 E7 0C # shl edi, 0x0C
81 C7 00 01 02 00 # add edi, 0x0020100 ; pproc_descriptor = &pproc_descriptor[current_process_num].current_dir
AC # lodsb ; first slash
AA # stosb
AC # lodsb
AA # stosb
3C 00 # cmp al, 0
75 04 # jne chdir_loop
31 C0 # xor eax, eax
74 0D # je chdir_finish ; if "/" don't add slash
#chdir_loop
AC # lodsb
AA # stosb
3C 00 # cmp al, 0
75 FA # jne chdir_loop
4F # dec edi
#:add_slash
B0 2F # mov al, '/'
AA # stosb
31 C0 # xor eax, eax
AA # stosb
#:chdir_finish
5F # pop edi
5E # pop esi
CB # retf
#----------------------------------------
#[84B7]
#:handle_syscall_exit
A1 1A 83 00 00 # mov eax, [&next_process_num]
48 # dec eax = current process
A3 1A 83 00 00 # mov [&next_process_num], eax
48 # dec eax = parent process
3C 00 # cmp al, 0
75 07 # jne not_first
#first process
8B 25 08 10 02 00 # mov esp, [0x021008]
CB # ret
#not_first
C1 E0 0C # shl eax, 0x0C
05 00 00 02 00 # add eax, 0x0020000 ; pproc_descriptor = &proc_descriptor[0]
89 58 18 # mov [eax+0x18], ebx ; save child exit code
8B 38 # mov edi, [eax] ; edi = pproc_descriptor->process_address
8B 70 1C # mov esi, [eax+0x1C] ; esi = pproc_descriptor->address_of_saved_process_memory
8B 48 20 # mov ecx, [eax+0x20] ; ecx = pproc_descriptor->length_of_process_memory
F3 A4 # rep movsb
8B 70 24 # mov esi, [eax+0x24] ; deallocate memory for saved process
89 35 AC 82 00 00 # mov [next_save_process_address], esi
8B 60 0C # mov esp, [eax+0xc] ; restore stack pointer
8B 70 14 # mov esi, [eax+0x14] ; restore brk pointer
89 70 04 # mov [eax+0x4], esi
8B 70 24 # mov esi, [eax+0x24] ; restore stack
89 E7 # mov edi, esp
8B 48 28 # mov ecx, [eax+0x28]
F3 A4 # rep movsb
# mimic syscall_fork's finish
B8 01 00 00 00 # mov eax, 0x1 ; process number for fork
5D # pop ebp
5F # pop edi
5E # pop esi
5A # pop edx
59 # pop ecx
5B # pop ebx
CB # ret ; go back to parent
#----------------------------------------
#[850B]
#:handle_syscall_waitpid
8B 35 1A 83 00 00 # mov esi, [&next_process_num]
4E # dec esi = current process
C1 E6 0C # shl esi, 0x0C
81 C6 18 00 02 00 # add esi, 0x00020018 ; pchild_code = &pproc_descriptor[current_process_num].child_exit_code
8B 06 # mov eax, [esi] ; get exit code
C1 E0 08 # shl eax, 0x08 ;
89 01 # mov [ecx], eax ; waitval = ret << 8
31 C0 # xor eax, eax
CB # ret
#----------------------------------------
#[8525]
#:handle_syscall_lseek
# inputs:
# ebx: fd
# ecx: value
# edx: method (0=SEEK_SET, 1=SEEK_CUR, 2=SEEK_END)
# outputs:
# eax: offset
#
56 # push esi
8B 35 1A 83 00 00 # mov esi, [&next_process_num]
4E # dec esi = current process
C1 E6 0C # shl esi, 0x0C
81 C6 04 02 02 00 # add esi, 0x0020204 ; pproc_descriptor = &pproc_descriptor[current_process_num].files[0].offset
83 FA 01 # cmp edx, 1
7F 13 # jg seek_end
7C 0A # jl seek_set
#:seek_cur
8B 04 DE # mov eax, [esi+ebx*8] ; get current_offset
01 C8 # add eax, ecx ; current_pos += offset
89 04 DE # mov [esi+ebx*8], eax ; set current_pos
5E # pop esi
CB # ret
#:seek_set
89 0C DE # mov [esi+ebx*8], ecx ; set current_pos
89 C8 # mov eax, ecx
5E # pop esi
CB # ret
#:seek_end
56 # push esi
53 # push ebx
9A 45 81 00 00 08 00 # call fd_to_file_index
BE 00 00 00 01 # mov esi, 0x01000000 ; pfile_descriptor = &file_descriptor[0]
C1 E3 04 # shl ebx, 04
01 DE # add esi, ebx
8B 46 08 # mov eax, [esi+0x8] ; get current_length
01 C8 # add eax, ecx ; current_length += offset
5B # pop ebx
5E # pop esi
89 04 DE # mov [esi+ebx*8], eax ; set current_offset
5E # pop esi
CB # ret
#----------------------------------------
#[856D]
#:handle_syscall_access
#inputs:
# ebx: path
# ecx: mode
9A 76 80 00 00 08 00 # call absolute_path
9A 13 81 00 00 08 00 # call find_file
83 F8 FF # cmp eax, -1
74 02 # je access_error_exit
31 C0 # xor eax, eax
#:access_error_exit
CB # ret
#----------------------------------------
#[8583]
#:handle_syscall_mkdir
#inputs:
# ebx: path
# ecx: mode
51 # push ecx
B9 41 00 00 00 # mov ecx, 0x41 (O_CREAT | O_WRONLY)
9A 94 7F 00 00 08 00 # call handle_syscall_open
83 F8 FF # cmp eax, -1
74 02 # je open_error_exit
31 C0 # xor eax, eax
#:open_error_exit
59 # pop ecx
CB # ret
#----------------------------------------
#[8599]
#:handle_syscall_getcwd
#inputs:
# ebx: buf
# ecx: buf size
#outputs:
# eax: buf
56 # push esi
57 # push edi
89 DF # mov edi, ebx
8B 35 1A 83 00 00 # mov esi, [&next_process_num]
4E # dec esi = current process
C1 E6 0C # shl esi, 0x0C
81 C6 00 01 02 00 # add esi, 0x0020100 ; pproc_descriptor = &pproc_descriptor[current_process_num].current_dir
# Handle root differently because we don't strip trailing slash
66 83 3E 2F # cmp word [esi], $002f ; is cwd == "/" ?
75 04 # jne copy_cwd
66 A5 # movsw ; copy "/" to buffer
EB 0A # jmp cleanup
# copy_cwd
AC # lodsb
AA # stosb
3C 00 # cmp al, 0
75 FA # jne copy_cwd
83 EF 02 # sub edi, 2 ; strip trailing slash
AA # stosb
# cleanup
89 D8 # mov eax, ebx
5F # pop edi
5E # pop esi
CB # ret
#------------------------------------------------------------
#[85C6]
#:strcmp
# inputs:
# esi: string1
# edi: string2
# outputs:
# zero flag
#
50 # push eax
53 # push ebx
56 # push esi
57 # push edi
#:check_byte
8A 06 # mov al, [esi]
8A 1F # mov bl, [edi]
38 D8 # cmp al, bl
75 0A # jne strcmp_finish
46 # inc esi
47 # inc edi
84 C0 # test al, al
75 F2 # jnz check_byte
84 DB # test bl, bl
75 EE # jnz check_byte
#:strcmp_finish
5F # pop edi
5E # pop esi
5B # pop ebx
58 # pop eax
CB # ret
#------
#[85E1]
#:io_char
00
00 # free
#----------------------------------------
#[85E3]
#:read
53 # push ebx
51 # push ecx
52 # push edx
B8 03 00 00 00 # mov eax, 3 ; syscall=read
B9 E1 85 00 00 # mov ecx, &io_char
BA 01 00 00 00 # mov edx, 1
CD 80 # int 80 syscall
3C 00 # cmp al, 0
74 07 # je read_finish
B4 01 # mov ah, 1
A0 E1 85 00 00 # mov al, &io_char
#:read_finish
5A # pop edx
59 # pop ecx
5B # pop ebx
CB # ret
#----------------------------------------
#[8606]
#:write
50 # push eax
53 # push ebx
51 # push ecx
52 # push edx
A2 E1 85 00 00 # mov &io_char, al
B8 04 00 00 00 # mov eax, 4 ; syscall=write
B9 E1 85 00 00 # mov ecx, &io_char
BA 01 00 00 00 # mov edx, 1 1 byte characters
CD 80 # int 80 syscall
5A # pop edx
59 # pop ecx
5B # pop ebx
58 # pop eax
CB # ret
#------
#[8625]
#:string_src
#s r c \0
73 72 63 00
#----------------------------------------
# src: create file from stdin
#
# Read an integer, N, in decimal from stdin.
# Read a space.
# Then, read a file name to create.
# Read a newline.
# Then, read N bytes from stdin and write to the new file.
#----------------------------------------
#[8629]
#:src
50 # push eax
53 # push ebx
51 # push ecx
52 # push edx
56 # push esi
57 # push edi
BE 25 86 00 00 # mov esi, string_src
9A 8F 7E 00 00 08 00 # call console_puts
9A E3 85 00 00 08 00 # call read 'r'
9A E3 85 00 00 08 00 # call read 'c'
9A E3 85 00 00 08 00 # call read ' '
31 C9 # xor ecx, ecx ; line count=0
#parse_line_count_loop:
9A E3 85 00 00 08 00 # call read
3C 20 # cmp al, ' '
74 0C # je got_count
6B C9 0A # imul ecx, ecx, 10 ; count = count * 10
2C 30 # sub al, 30
0F B6 C0 # movzx eax, al
01 C1 # add ecx, eax ; count += digit
EB E9 # jmp parse_line_count_loop
#:got_count
# clear arguments
51 # push ecx
31 C0 # xor eax, eax
BA 00 00 D0 04 # mov edx, 0x04D00000
B9 00 08 00 00 # mov ecx, 0x00000800
#:src_args_zeroloop
88 02 # mov [edx], al
42 # inc edx
49 # dec ecx
75 FA # jnz src_args_zeroloop
59 # pop ecx
51 # push ecx
B9 00 00 D0 04 # mov ecx, 0x04D00000
#:get_filename_loop
9A E3 85 00 00 08 00 # call read
3C 0A # cmp al, '\n'
74 05 # je got_filename
88 01 # mov [ecx], al
41 # inc ecx
EB F0 # jmp get_file_name_loop
59 # pop ecx
#:got_filename
BE 00 00 D0 04 # mov esi, 0x04D00000
9A 8F 7E 00 00 08 00 # call console_puts
# open filename for write
51 # push ecx
BB 00 00 D0 04 # mov ebx, 0x04D00000
B8 05 00 00 00 # mov eax, 5 ; syscall_open
B9 41 02 00 00 # mov ecx, 0x00000241 O_TRUNC (0x200) | O_CREAT (0x40) | O_WRONLY (0x1)
BA 80 01 00 00 # mov edx, 0x00000180 S_IRUSR (0x100) | S_IWUSR (0x80)
CD 80 # int 80
59 # pop ecx
89 C2 # mov edx, eax
# edx has the open file number
#:readwrite_loop
85 C9 # test ecx, ecx
74 1C # jz src_finish
BF 00 02 04 00 # mov edi, 0x00040200 ; scratch buffer
57 # push edi ; save buffer address
31 DB # xor ebx, ebx ; ebx=0=stdin
9A E3 85 00 00 08 00 # call read
89 D3 # mov ebx, edx ; prepare to write
5E # pop esi ; restore buffer address to esi
9A 06 86 00 00 08 00 # call write
49 # dec ecx ; count--
EB E0 # jmp read_write_loop
#:src_finish
5F # pop edi
5E # pop esi
5A # pop edx
59 # pop ecx
5B # pop ebx
58 # pop eax
CB # ret
#------
#[86E1]
#:hex0_str
#h e x 0 \0
68 65 78 30 00
#------------------------------------------------------------
#[86E6]
#:hex0
53 # push ebx
56 # push esi
57 # push edi
BE E1 86 00 00 # mov esi, hex0_str
9A 8F 7E 00 00 08 00 # call console_puts
# read "ex0 '
B1 04 # mov cl, 4
#:ex0_loop
9A E3 85 00 00 08 00 # call read
FE C9 # dec cl
75 F5 # jnz ex0_loop
# clear arguments
31 C0 # xor eax, eax
BA 00 00 D0 04 # mov edx, 0x04D00000
B9 00 08 00 00 # mov ecx, 0x00000800
#:hex0_args_zeroloop
88 02 # mov [edx], al
42 # inc edx
49 # dec ecx
75 FA # jnz hex0_args_zeroloop
BA 00 00 D0 04 # mov edx, 0x04D00000
#:get_file_name1_loop
9A E3 85 00 00 08 00 # call read
9A 6F 7E 00 00 08 00 # call console_putc
3C 20 # cmp al, ' '
74 05 # je got_filename1
88 02 # mov [edx], al
42 # inc edx
EB E9 # jmp get_file_name1_loop
#:got_filename1
BA 00 04 D0 04 # mov edx, 0x04D00400
#:get_file_name2_loop
9A E3 85 00 00 08 00 # call read
9A 6F 7E 00 00 08 00 # call console_putc
3C 0A # cmp al, '\n'
74 05 # je got_filename2
88 02 # mov [edx], al
42 # inc edx
EB E9 # jmp get_file_name2_loop
# open filename1 for read
BB 00 00 D0 04 # mov ebx, 0x04D00000
B8 05 00 00 00 # mov eax, 5 ; syscall_open
B9 00 00 00 00 # mov ecx, 0x00000000
CD 80 # int 80
50 # push eax ; save read filenum
# open filename2 for write
BB 00 04 D0 04 # mov ebx, 0x04D00400
B8 05 00 00 00 # mov eax, 5 ; syscall_open
B9 41 02 00 00 # mov ecx, 0x00000241 O_TRUNC (0x200) | O_CREAT (0x40) | O_WRONLY (0x1)
BA 80 01 00 00 # mov edx, 0x00000180 S_IRUSR (0x100) | S_IWUSR (0x80)
CD 80 # int 80
89 C2 # mov edx, eax
59 # pop ecx ; restore read filenum
# this flag is set after the first digit is seen
31 DB # xor ebx, ebx
#------
#[8779]
#:hex0_read_loop
53 # push ebx
89 CB # mov ebx, ecx
9A E3 85 00 00 08 00 # call read
5B # pop ebx
84 e4 # test ah, ah
75 04 # jnz check_command
5F # POP_DI
5E # POP_SI
5B # POP_BX
CB # RETF
#:check_command
3C 23 # cmp al, '#'
74 28 # jz skip_comment
3C 3B # cmp ';'
74 24 # jz skip_comment
3C 66 # cmp al, 'f'
7F D1 # jg hex0_read_loop
3C 61 # cmp al, 'a'
7C 04 # jl maybe_upper
# Handle a to f
2C 57 # sub al, 'a'-10 == 87 = 0x57
EB 29 # jmp maybe_store
#:maybe_upper
3C 46 # cmp al, 'F'
7F D5 # jg hex0_read_loop
3C 41 # cmp al, 'A'
7C 04 # jl maybe_digit
# Handle A to F
2C 37 # sub al, 'A'-10 == 55 = x37
EB 1D # jmp maybe_store
#:maybe_digit
3C 39 # cmp al, '9'
7F C9 # jg hex0_read_loop
3C 30 # cmp al, '0'
7C C5 # jl hex0_read_loop
# Handle 0 to 9
2C 30 # sub al, '0' == x30
EB 11 # jmp maybe_store
#:skip_comment
53 # push ebx
89 CB # mov ebx, ecx
9A E3 85 00 00 08 00 # call read
5B # pop ebx
3C 0A # cmp al, '\n'
75 F1 # jnz skip_comment
EB B0 # jmp hex0_read_loop
# only store on second digit
#:maybe_store
84 DB # test bl, bl
75 09 # jnz second_digit
# If on first digit, record and keep going
#:first_digit
C0 E0 04 # shl al, 4
88 C7 # mov bh, al
FE C3 # inc bl
EB A3 # jmp hex0_read_loop
# If on second digit, store and clear state
#:second_digit
08 C7 # or bh, al
88 F8 # mov al, bh
53 # push ebx
89 D3 # mov ebx, edx
9A 06 86 00 00 08 00 # call write
5B # pop ebx
31 DB # xor bx, bx
EA 79 87 00 00 08 00 # jmp hex0_read_loop
#------
#[87EE]
#:cmd_args
00 00 D0 04
00 04 D0 04
#------
#[87F6]
#:cmd_env
00 00 00 00
#------------------------------------------------------------
#[87FA]
#:handle_other_command
50 # push eax
53 # push ebx
51 # push ecx
52 # push edx
56 # push esi
# clear arguments
BA 00 00 D0 04 # mov edx, 0x04D00000
88 02 # mov [edx], al
42 # inc edx
31 C0 # xor eax, eax
B9 FF 07 00 00 # mov ecx, 0x000007FF
#other_args_zeroloop
88 02 # mov [edx], al
42 # inc edx
49 # dec ecx
75 FA # jnz other_args_zeroloop
BA 01 00 D0 04 # mov edx, 0x04D00001
#:get_program_name
9A E3 85 00 00 08 00 # call read
3C 20 # cmp al, ' '
74 05 # je got_program_name
88 02 # mov [edx], al
42 # inc edx
EB F0 # jmp get_program_name
#got_program_name
BA 00 04 D0 04 # mov edx, 0x04D00400
#get_argument1_loop
9A E3 85 00 00 08 00 # call read
3C 0A # cmp al, '\n'
74 05 # je got_argument1
88 02 # mov [edx], al
42 # inc edx
EB F0 # jmp get_argument1_loop
#:got_argument1
BE 00 00 D0 04 # mov esi, program
9A 8F 7E 00 00 08 00 # call console_puts
BE 00 04 D0 04 # mov esi, arg1
9A 8F 7E 00 00 08 00 # call console_puts
BB 00 00 D0 04 # mov ebx, program_name
B9 EE 87 00 00 # mov ecx, cmd_args
BA F6 87 00 00 # mov edx, cmd_env
9A 1E 83 00 00 08 00 # call handle_syscall_execve
5E # pop esi
5A # pop edx
59 # pop ecx
5B # pop ebx
58 # pop eax
CB
#------
#[8872]
#:str_build_finished
#B u i l d f i n i s h e d . \0
42 75 69 6C 64 20 66 69 6E 69 73 68 65 64 2E 00
#------
#[8882]
#:str_error_no_writes
#E R R O R : n o h d a w r i t e s ! \0
45 52 52 4F 52 3A 20 6E 6F 20 68 64 61 20 77 72 69 74 65 73 21 00
#------
#[8898]
#:str_dev_hda
#/ d e v / h d a \0
2F 64 65 76 2F 68 64 61 00
#------------------------------------------------------------
#[88A1]
#:internalshell
# Start reading stdin from sector 8, cyl 0, head 0
C7 05 00 00 00 01 08 00 00 00 # mov word [0x01000000], 0x00000008
# start at "end of sector" to trigger an initial sector read
66 C7 05 04 00 00 01 FF 01 # mov word [0x01000004], 0x01FF
66 C7 05 00 01 02 00 2F 00 # mov [0x00020100], 0x002F ; proc[0].cwd = "/"
66 C7 05 00 11 02 00 2F 00 # mov [0x00021100], 0x002F ; proc[1].cwd = "/"
# clear file descriptors for process 0
BF 00 02 02 00 # mov edi, 0x00020200
B9 00 0E 00 00 # mov ecx, 0x00000E00
31 C0 # xor eax, eax
F3 AA # rep stosb
# read from stdin
31 DB # xor ebx, ebx
#:process_command
9A E3 85 00 00 08 00 # call read
3C 00 # cmp al, 0
74 23 # je build_finished
#:check_src_command
3C 73 # cmp al, 's'
75 09 # jne check_hex0_command
#:handle_src_command
9A 29 86 00 00 08 00 # call src
EB E8 # jmp process_command
#:check_hex0_command
3C 68 # cmp al, 'h'
75 09 # jne call_handle_other_command
#:handle_hex0_command
9A E6 86 00 00 08 00 # call hex0
EB DB # jmp process_command
#:call_handle_other_command
9A FA 87 00 00 08 00 # call handle_other_command
EB D2 # jmp process_command
#:build_finished
BE 72 88 00 00 # mov esi, str_build_finished
9A 8F 7E 00 00 08 00 # call console_puts
# copy memory file /dev/hda to the boot disk
BB 98 88 00 00 # mov ebx, str_dev_hda
9A 13 81 00 00 08 00 # call find_file
83 f8 ff # cmp eax, -1
75 17 # jne ok_exit
#:error_exit
BE 82 88 00 00 # mov esi, str_error_no_write
9A 8F 7E 00 00 08 00 # call console_puts
# one space to flush last line
B0 20 # mov al, 20
9A 6F 7E 00 00 08 00 # call console_putc
EB 62 # jmp shell_reboot
#:ok_exit
# get file address to read
C1 E0 04 # shl eax, 04
05 00 00 00 01 # add eax, 0x01000000
8B 70 04 # mov esi, [eax+4] ; file_address
8B 58 08 # mov ebx, [eax+8] ; bytes_to_write = file_length
# print length
89 D8 # mov eax, ebx
B9 04 00 00 00 # mov ecx, 4
#:shift_loop
C1 C8 18 # ror eax, 24
9A 7F 7E 00 00 08 00 # call console_put_hex
49 # dec ecx
75 F3 # jnz shift_loop
B0 0A # mov al, 0A
9A 6F 7E 00 00 08 00 # call console_putc
# set starting disk location to write
31 C9 # xor ecx, ecx
41 # inc ecx
B6 00 # mov dh, 0
#:sector_loop
# copy 512 bytes from file to 16 bit buffer
51 # push ecx ; save disk location
BF 00 A0 00 00 # mov edi, 0x000A000
B9 00 02 00 00 # mov ecx, 0x200
F3 A4 # rep movsb
59 # pop ecx
# now write from 16 bit buffer
56 # push esi ; save our location in the file
BE 00 A0 00 00 # mov esi, 0x000A000
B8 01 00 00 00 # mov eax, 0x0001 ; num_sectors = 1
9A BD 7E 00 00 08 00 # call write_sectors
5E # pop esi
81 EB 00 02 00 00 # sub ebx, 0x200 ; bytes_to_write -= 512
7F D7 # jg sector_loop
B0 20 # mov al, 20
9A 6F 7E 00 00 08 00 # call console_putc
#:shell_reboot
EA CD 7E 00 00 08 00 # jmp reboot
# sector padding
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00