1
0
mirror of https://gitlab.os-k.eu/os-k-team/kvisc.git synced 2023-08-25 14:05:46 +02:00
kvisc/vm/ob/doskrnl.k

1631 lines
25 KiB
Plaintext
Raw Normal View History

2019-08-21 18:47:03 +02:00
# 1 "doskrnl.k"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "doskrnl.k"
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
__sysmain:
jmp start
# 1 "crt/crt.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
;
; Limits
;
# 31 "crt/crt.k"
;
; Magic numbers
;
;
; CRT librairies
;
# 1 "./crt/sys.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
;
; SHUTDOWN syscall
;
; End virtual machine
;
Sys.Shutdown := 0x00
;
; EXIT syscall
;
; Return to COMMAND.COM
;
Sys.Exit := 0x01
;
; EXEC syscall
;
; IN
; ax0 = new RIP to load
; ax1 = frame ID to switch to
;
; New frame ID must be higher than current
; frame ID, and cannot be below 5
;
; New CR1 is set so that new RIP corresponds to 1MB
;
Sys.ExecuteInFrame := 0x02
; FIND syscalls
;
; Find file on disk
;
; IN
; ax0 = address of name buffer
; ax1 = size of name buffer
;
; OUT
; rax = # of bytes written in name buffer
; rdx = size of file
;
;
Sys.FindFirst := 0x20
Sys.FindNext := 0x21
;
; OPEN syscall
;
; IN
; ax0 = name string
;
; OUT
; rax = handle of file, or <0 if couldn't open
;
Sys.OpenFile := 0x30
;
; CREATE syscall
;
; IN
; ax0 = name string
;
; OUT
; rax = 0 on success, or <0 if couldn't open
;
Sys.CreateFile := 0x31
;
; REMOVE syscall
;
; IN
; ax0 = name string
;
; OUT
; rax = 0 on success, or <0 if couldn't open
;
Sys.RemoveFile := 0x32
;
; CLOSE syscall
;
; IN
; ax0 = file handle
;
Sys.CloseFile := 0x35
;
; READ syscall
;
; IN
; ax0 = file handle
; ax1 = buffer address
; ax2 = buffer size
;
; OUT
; rax = number of bytes read, <0 on error
;
Sys.ReadFile := 0x38
; Halt mode
Sys.EnterHaltMode := 0x999
# 43 "crt/crt.k" 2
# 1 "./crt/str.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
;
; int strnlen(char *, int)
;
strnlen:
2019-09-08 19:04:07 +02:00
mov ecx, ax1
2019-08-21 18:47:03 +02:00
scasb ax0, zero
2019-09-08 19:04:07 +02:00
sub eax, ax1, ecx
2019-08-21 18:47:03 +02:00
ret
;
; int strlen(char *)
;
strlen:
2019-09-08 19:04:07 +02:00
mov ecx, 0x7AFFFFFF
mov edx, ecx
2019-08-21 18:47:03 +02:00
scasb ax0, zero
2019-09-08 19:04:07 +02:00
sub eax, edx, ecx
2019-08-21 18:47:03 +02:00
ret
;
; void strcpy(char *, const char *)
;
strcpy:
.l:
2019-09-08 19:04:07 +02:00
mov ecx, b[ax1]
mov b[ax0], ecx
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
jecxz .r
2019-08-21 18:47:03 +02:00
inc ax0
inc ax1
jmp .l
.r:
ret
;
; void strncpy(char *, const char *, int)
;
strncpy:
2019-09-08 19:04:07 +02:00
mov ecx, ax2
jecxz .r
2019-08-21 18:47:03 +02:00
.l:
mov b[ax0], b[ax1]
inc ax0
inc ax1
loop .l
.r:
ret
;
; void strnzcpy(char *, const char *, int)
;
strnzcpy:
2019-09-08 19:04:07 +02:00
mov ecx, ax2
jecxz .r
2019-08-21 18:47:03 +02:00
.l:
2019-09-08 19:04:07 +02:00
mov eax, b[ax1]
mov b[ax0], eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
jeaxz .r
2019-08-21 18:47:03 +02:00
inc ax0
inc ax1
loop .l
.z:
nul b[ax0]
.r:
ret
;
; int strcmp(const char *str1, const char *str2)
;
; Returns:
; 0 if the contents of both strings are equal
; >0 if the first character that does not match has a greater value in str1 than in str2
; <0 if the first character that does not match has a lower value in str1 than in str2
;
strcmp:
2019-09-08 19:04:07 +02:00
nul esi
2019-08-21 18:47:03 +02:00
.l:
2019-09-08 19:04:07 +02:00
movzx eax, b[ax0+esi]
movzx edx, b[ax1+esi]
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
bne eax, edx, .r
2019-08-21 18:47:03 +02:00
; both zero?
2019-09-08 19:04:07 +02:00
add ecx, eax, edx
jecxz .r
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
inc esi
2019-08-21 18:47:03 +02:00
jmp .l
.r:
2019-09-08 19:04:07 +02:00
sub eax, edx
2019-08-21 18:47:03 +02:00
ret
;
; int strncmp(const char *str1, const char *str2, int maxn)
;
strncmp:
2019-09-08 19:04:07 +02:00
mov ecx, ax2
jecxz .r
2019-08-21 18:47:03 +02:00
.l:
2019-09-08 19:04:07 +02:00
movzx eax, b[ax0]
movzx edx, b[ax1]
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
bne eax, edx, .r
2019-08-21 18:47:03 +02:00
inc ax0
inc ax1
loop .l
.r:
2019-09-08 19:04:07 +02:00
sub eax, edx
2019-08-21 18:47:03 +02:00
ret
;
; char *strchrnul(const char *str, int ch)
;
strchrnul:
2019-09-08 19:04:07 +02:00
mov ecx, 0x7AFFFFFF
2019-08-21 18:47:03 +02:00
scasb ax0, ax1
2019-09-08 19:04:07 +02:00
mov eax, ax0
2019-08-21 18:47:03 +02:00
ret
;
; char *strchr(const char *str, int ch)
;
strchr:
2019-09-08 19:04:07 +02:00
mov ecx, 0x7AFFFFFF
2019-08-21 18:47:03 +02:00
scasb ax0, ax1
bnz b[ax0], .r
2019-09-08 19:04:07 +02:00
nul eax
2019-08-21 18:47:03 +02:00
ret
.r:
2019-09-08 19:04:07 +02:00
mov eax, ax0
2019-08-21 18:47:03 +02:00
ret
;
; void strrev(char *buf, const char *str)
;
; buf and src must NOT overlap
;
strrev:
bzr b[ax1], .z
; save str's location
2019-09-08 19:04:07 +02:00
mov esi, ax1
2019-08-21 18:47:03 +02:00
; go to str's end, just before
; the null terminator
2019-09-08 19:04:07 +02:00
mov ecx, 0x7AFFFFFF
2019-08-21 18:47:03 +02:00
scasb ax1, zero
dec ax1
.l:
; copy, going backward though str
; and forward through buf
mov b[ax0], b[ax1]
2019-09-08 19:04:07 +02:00
beq ax1, esi, .r
2019-08-21 18:47:03 +02:00
inc ax0
dec ax1
jmp .l
.r:
nul b[ax0+1]
ret
.z:
nul b[ax0]
ret
;
; void strrev2(char *str)
;
; Inverses str
;
strrev2:
bzr b[ax0], .r
mov ax1, ax0
; go to str's end, just before
; the null terminator
2019-09-08 19:04:07 +02:00
mov ecx, 0x7AFFFFFF
2019-08-21 18:47:03 +02:00
scasb ax1, zero
dec ax1
; increase ax0 while decreasing ax1, performing exchanges
.l:
blteu ax1, ax0, .r
xchg b[ax0], b[ax1]
inc ax0
dec ax1
jmp .l
.r:
ret
# 44 "crt/crt.k" 2
# 1 "./crt/mem.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
;
; void memcpy(void *, const void *, int)
;
memcpy:
2019-09-08 19:04:07 +02:00
mov ecx, ax2
jecxz .r
2019-08-21 18:47:03 +02:00
.l:
2019-09-08 19:04:07 +02:00
sub edx, ax2, ecx
mov b[ax0+edx], b[ax1+edx]
2019-08-21 18:47:03 +02:00
loop .l
.r:
ret
;
; void memzero(void *, int)
;
memzero:
2019-09-08 19:04:07 +02:00
mov ecx, ax1
jecxz .r
2019-08-21 18:47:03 +02:00
.l:
nul b[ax0]
inc ax0
loop .l
.r:
ret
# 45 "crt/crt.k" 2
# 1 "./crt/time.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
;
; struct TIME
; {
; byte sec; +0 (0-59)
; byte min; +1 (0-59)
; byte hour; +2 (0-23)
; byte mday; +3 (0-31)
; byte month; +4 (0-11)
; byte; +5 (pad)
; word year; +6 (0-65536)
; word yday; +8 (0-365)
; word; +10 (pad)
; dword; +12 (pad)
; } 16 bytes
;
;
; int DaysInYear(int year)
;
DaysInYear:
2019-09-08 19:04:07 +02:00
mov eax, 365
2019-08-21 18:47:03 +02:00
; divisible by 4?
2019-09-08 19:04:07 +02:00
rem ecx, ax0, 4
jecxnz .end
2019-08-21 18:47:03 +02:00
; divisible by 100?
2019-09-08 19:04:07 +02:00
rem ecx, ax0, 100
jecxnz .leap
2019-08-21 18:47:03 +02:00
; divisible by 400?
2019-09-08 19:04:07 +02:00
rem ecx, ax0, 400
jecxnz .end
2019-08-21 18:47:03 +02:00
.leap:
2019-09-08 19:04:07 +02:00
inc eax
2019-08-21 18:47:03 +02:00
.end:
ret
;
; TIME *GetTimeUTC(void)
;
GetTimeUTC:
ytime
2019-09-08 19:04:07 +02:00
mov edi, .buf
2019-08-21 18:47:03 +02:00
; seconds
2019-09-08 19:04:07 +02:00
rem esi, eax, 60
mov b[edi], esi
2019-08-21 18:47:03 +02:00
; minutes
2019-09-08 19:04:07 +02:00
div esi, eax, 60
rem esi, 60
mov b[edi+1], esi
2019-08-21 18:47:03 +02:00
; hours
2019-09-08 19:04:07 +02:00
div esi, eax, 3600
rem esi, 24
mov b[edi+2], esi
2019-08-21 18:47:03 +02:00
; month days
2019-09-08 19:04:07 +02:00
div esi, eax, 3600*24
mov b[edi+3], esi
2019-08-21 18:47:03 +02:00
; month
2019-09-08 19:04:07 +02:00
mov b[edi+4], rbx
2019-08-21 18:47:03 +02:00
; years
2019-09-08 19:04:07 +02:00
mov w[edi+6], ecx
2019-08-21 18:47:03 +02:00
;
; ydays (TODO)
;
2019-09-08 19:04:07 +02:00
mov eax, .buf
2019-08-21 18:47:03 +02:00
ret
.buf = [24]
# 46 "crt/crt.k" 2
# 1 "./crt/fmt/ltostr.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
;
; void itoa(char *buf, int num, int base)
;
itoa:
mov ax3, 1
jmp ltostr
;
; void utoa(char *buf, int num, int base)
;
utoa:
nul ax3
jmp ltostr
;
; void ltostr(char *buf, int num, int base, bool signed)
;
ltostr:
2019-09-08 19:04:07 +02:00
mov eax, ax0
nul ecx
2019-08-21 18:47:03 +02:00
; make sure base is in [2, 32]
bltu ax2, 2, .bad
bltu 36, ax2, .bad
; deal with zero
bzr ax1, .is_zero
; deal with base 10 signedness
bzr ax3, .conv
bne ax2, 10, .conv ; base 10
2019-09-08 19:04:07 +02:00
shr ecx, ax1, 63 ; extract ax1 sign
jecxz .conv
2019-08-21 18:47:03 +02:00
neg ax1 ; NEG if negative
; main loop
.conv:
bzr ax1, .fini
2019-09-08 19:04:07 +02:00
rem edx, ax1, ax2 ; ax1 % base
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
blt 9, edx, .nondec ; edx > 9 ?
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
add edx, '0'
2019-08-21 18:47:03 +02:00
jmp .next
.nondec:
2019-09-08 19:04:07 +02:00
add edx, 55 ; 'A' - 10
2019-08-21 18:47:03 +02:00
.next:
2019-09-08 19:04:07 +02:00
mov b[ax0], edx
2019-08-21 18:47:03 +02:00
inc ax0
div ax1, ax2
jmp .conv
; add minus flag, null-terminate and reverse
.fini:
2019-09-08 19:04:07 +02:00
jecxz .cxz
2019-08-21 18:47:03 +02:00
mov b[ax0], '-'
inc ax0
.cxz:
nul b[ax0]
2019-09-08 19:04:07 +02:00
call strrev2, eax
2019-08-21 18:47:03 +02:00
ret
;
; exceptional cases
;
.bad:
mov b[ax0], 0
ret
.is_zero:
mov b[ax0], 48 ; '0'
mov b[ax0+1], 0
ret
# 48 "crt/crt.k" 2
# 1 "./crt/fmt/strtol.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
;
; int strtol(const char *str, int base)
;
2019-09-08 19:04:07 +02:00
; eax = integer extracted from str
; edx = pointer to first invalid byte
2019-08-21 18:47:03 +02:00
;
strtol:
mov ax2, 1
jmp strtoq
;
; int strtoul(const char *str, int base)
;
2019-09-08 19:04:07 +02:00
; eax = integer extracted from str
; edx = pointer to first invalid byte
2019-08-21 18:47:03 +02:00
;
strtoul:
nul ax2
jmp strtoq
;
; int strtoq(const char *str, int base, bool signed)
;
; guesses base when 'base'=0
;
strtoq:
2019-09-08 19:04:07 +02:00
nul eax, esi
mov edx, ax0
2019-08-21 18:47:03 +02:00
; make sure base is in [2, 32]
beq ax1, 1, .bad
bltu 36, ax1, .bad
; empty string?
2019-09-08 19:04:07 +02:00
bzr b[edx], .done
2019-08-21 18:47:03 +02:00
.skip_spc:
2019-09-08 19:04:07 +02:00
bne b[edx], ' ', .no_spc
inc edx
2019-08-21 18:47:03 +02:00
jmp .skip_spc
.no_spc:
; skip +
2019-09-08 19:04:07 +02:00
bne b[edx], '+', .no_plus
inc edx
2019-08-21 18:47:03 +02:00
.no_plus:
; unsigned?
bzr ax2, .unsigned
; parse '-'
2019-09-08 19:04:07 +02:00
bne b[edx], '-', .unsigned
inc edx
mov esi, 1
2019-08-21 18:47:03 +02:00
.unsigned:
; base 0
bzr ax1, .base_0
; base prefix?
2019-09-08 19:04:07 +02:00
bne b[edx], '0', .main_loop
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
inc edx
movzx ecx, b[edx]
2019-08-21 18:47:03 +02:00
; "0x"/"0b" prefix
2019-09-08 19:04:07 +02:00
jecxz .done ; "0"
beq ecx, 'x', .parsed_0x
beq ecx, 'b', .parsed_0b
2019-08-21 18:47:03 +02:00
; may be octal, but we don't care
; we accept "0110101010" (despite base=2) for instance
jmp .main_loop
.parsed_0x:
; are we in base 16?
2019-09-08 19:04:07 +02:00
; if not, leave eax = 0 and *edx = 'x'
2019-08-21 18:47:03 +02:00
bne ax1, 16, .done
; else
2019-09-08 19:04:07 +02:00
inc edx
2019-08-21 18:47:03 +02:00
jmp .main_loop
.parsed_0b:
; are we in base 2?
2019-09-08 19:04:07 +02:00
; if not, leave eax = 0 and *edx = 'b'
2019-08-21 18:47:03 +02:00
bne ax1, 2, .done
; else
2019-09-08 19:04:07 +02:00
inc edx
2019-08-21 18:47:03 +02:00
jmp .main_loop
.base_0:
; guess base
2019-09-08 19:04:07 +02:00
beq b[edx], '0', .b0_not10
2019-08-21 18:47:03 +02:00
; must be base 10
mov ax1, 10
jmp .main_loop
.b0_not10:
2019-09-08 19:04:07 +02:00
inc edx
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
bne b[edx], 'x', .b0_not16
inc edx
2019-08-21 18:47:03 +02:00
mov ax1, 16
jmp .main_loop
.b0_not16:
2019-09-08 19:04:07 +02:00
bne b[edx], 'b', .b0_not2
inc edx
2019-08-21 18:47:03 +02:00
mov ax1, 2
jmp .main_loop
.b0_not2:
; take octal by default
mov ax1, 8
.main_loop:
2019-09-08 19:04:07 +02:00
movzx ecx, b[edx]
inc edx
2019-08-21 18:47:03 +02:00
; between 0 and 9?
2019-09-08 19:04:07 +02:00
bltu ecx, '0', .done
bltu '9', ecx, .not_digit10
2019-08-21 18:47:03 +02:00
; yes
2019-09-08 19:04:07 +02:00
sub ecx, '0'
2019-08-21 18:47:03 +02:00
jmp .next
.not_digit10:
2019-09-08 19:04:07 +02:00
bltu ecx, 'A', .done
bltu 'Z', ecx, .not_digitAZ
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
sub ecx, 55 ; 'A' - 10
2019-08-21 18:47:03 +02:00
jmp .next
.not_digitAZ:
2019-09-08 19:04:07 +02:00
bltu ecx, 'a', .done
bltu 'z', ecx, .done
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
sub ecx, 87 ; 'a' - 10
2019-08-21 18:47:03 +02:00
jmp .next
.next:
; too large for base?
2019-09-08 19:04:07 +02:00
blteu ax1, ecx, .done
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
mul eax, ax1
add eax, ecx
2019-08-21 18:47:03 +02:00
jmp .main_loop
.done:
; negative?
2019-09-08 19:04:07 +02:00
bzr esi, .r
2019-08-21 18:47:03 +02:00
; yes
2019-09-08 19:04:07 +02:00
neg eax
2019-08-21 18:47:03 +02:00
.r:
ret
.bad:
ret
# 49 "crt/crt.k" 2
# 1 "./crt/fmt/doprnt.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
;
; typedef int (*PUTC)(int ch)
; int doprnt(PUTC putc, int n, const char *fmt, va_list ap)
;
doprnt:
2019-09-08 19:04:07 +02:00
push ebp
mov ebp, esp
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
push nx0, nx1, nx2
push nx3, nx4, nx5
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
mov nx0, ax2 ; fmt
mov nx2, ax3 ; va_list
mov nx3, ax1 ; n
mov nx5, ax0 ; putc
nul nx4 ; return value
2019-08-21 18:47:03 +02:00
.main_loop:
; find '%' or null-terminator
2019-09-08 19:04:07 +02:00
mov ecx, 0x7AFFFFFF
mov nx1, nx0
scasb nx1, '%'
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
; everything below nx1 is a regular character; print it
2019-08-21 18:47:03 +02:00
.print_regular:
2019-09-08 19:04:07 +02:00
beq nx0, nx1, .check_modf
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call .doput, b[nx0]
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
inc nx0
2019-08-21 18:47:03 +02:00
jmp .print_regular
.check_modf:
; did we find a '%' ?
; if not, then we found fmt's null-terminator; we're done
2019-09-08 19:04:07 +02:00
bne b[nx0], '%', .epilogue
2019-08-21 18:47:03 +02:00
; we did find a modifier / '%'
2019-09-08 19:04:07 +02:00
mov eax, b[nx0+1]
add nx0, 2
beq eax, 's', .modf_s
beq eax, 'c', .modf_c
beq eax, 'p', .modf_p
beq eax, 'x', .modf_x
beq eax, 'd', .modf_d
beq eax, 'o', .modf_o
beq eax, 'b', .modf_b
beq eax, '%', .modf_percent
2019-08-21 18:47:03 +02:00
; unrecognized
jmp .bad_modifier
.modf_s:
; get string address
2019-09-08 19:04:07 +02:00
mov nx1, q[nx2]
add nx2, 8
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
bzr nx1, .nullstring
2019-08-21 18:47:03 +02:00
.print_string:
2019-09-08 19:04:07 +02:00
movzx ax0, b[nx1]
2019-08-21 18:47:03 +02:00
bzr ax0, .main_loop
2019-09-08 19:04:07 +02:00
inc nx1
2019-08-21 18:47:03 +02:00
call .doput
jmp .print_string
.modf_c:
2019-09-08 19:04:07 +02:00
call .doput, q[nx2]
add nx2, 8
2019-08-21 18:47:03 +02:00
jmp .main_loop
.modf_p:
call .doput, '0'
call .doput, 'x'
; Fallthrough
.modf_x:
mov ax2, 16
jmp .print_number
.modf_d:
mov ax2, 10
jmp .print_number
.modf_o:
mov ax2, 8
jmp .print_number
.modf_b:
mov ax2, 2
jmp .print_number
.print_number:
; allocate itoa conversion buffer
2019-09-08 19:04:07 +02:00
sub esp, 80
mov nx1, esp
2019-08-21 18:47:03 +02:00
; assume modifier already set up ax2
2019-09-08 19:04:07 +02:00
call itoa, esp, q[nx2]
add nx2, 8
2019-08-21 18:47:03 +02:00
.print_itoa_buf:
2019-09-08 19:04:07 +02:00
movzx ax0, b[nx1]
2019-08-21 18:47:03 +02:00
bzr ax0, .pib_end_loop
2019-09-08 19:04:07 +02:00
inc nx1
2019-08-21 18:47:03 +02:00
call .doput
jmp .print_itoa_buf
.pib_end_loop:
2019-09-08 19:04:07 +02:00
add esp, 80
2019-08-21 18:47:03 +02:00
jmp .main_loop
.modf_percent:
call .doput, '%'
jmp .main_loop
.bad_modifier:
; print "%?" to clearly indicate that something is wrong
call .doput, '%'
call .doput, '?'
jmp .main_loop
.nullstring:
; %s was passed a NULL
call .doput, '('
call .doput, 'n'
call .doput, 'u'
call .doput, 'l'
call .doput, 'l'
call .doput, ')'
jmp .main_loop
.epilogue:
2019-09-08 19:04:07 +02:00
mov eax, nx4
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
pop nx5, nx4
pop nx3, nx2
pop nx1, nx0
2019-08-21 18:47:03 +02:00
leave
ret
;
; prints ax0
;
.doput:
; update print count
2019-09-08 19:04:07 +02:00
inc nx4
2019-08-21 18:47:03 +02:00
; if n==0, don't print
; we follow the C convention that sprintf()-like functions
; should return the number of characters that would have
; been printed/written if 'n' were big enough
2019-09-08 19:04:07 +02:00
bzr nx3, .r
2019-08-21 18:47:03 +02:00
; decrement n and print
2019-09-08 19:04:07 +02:00
dec nx3
call nx5
2019-08-21 18:47:03 +02:00
; did putc fail?
2019-09-08 19:04:07 +02:00
jeaxz .r
2019-08-21 18:47:03 +02:00
; yes, so artificially set n=0
2019-09-08 19:04:07 +02:00
nul nx3
2019-08-21 18:47:03 +02:00
.r:
ret
# 50 "crt/crt.k" 2
# 1 "./crt/fmt/printf.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
;
; int putc(int ch)
;
putc:
prn ax0
2019-09-08 19:04:07 +02:00
nul eax
2019-08-21 18:47:03 +02:00
ret
;
; int printf(const char *fmt, ...)
;
printf:
mov ax2, ax0
mov ax0, putc
mov ax1, 0x7AFFFFFF
2019-09-08 19:04:07 +02:00
add ax3, esp, 8
2019-08-21 18:47:03 +02:00
jmp doprnt
;
; int nprintf(const char *fmt, int n, ...)
;
nprintf:
mov ax2, ax0
mov ax0, putc
2019-09-08 19:04:07 +02:00
add ax3, esp, 8
2019-08-21 18:47:03 +02:00
jmp doprnt
;
; Print a string
2019-09-08 19:04:07 +02:00
; Guaranteed to only affect ecx and ax0
2019-08-21 18:47:03 +02:00
;
print:
.l:
2019-09-08 19:04:07 +02:00
movzx eax, b[ax0]
jeaxz .r
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
prn eax
2019-08-21 18:47:03 +02:00
inc ax0
jmp .l
.r:
ret
;
; Print exactly ax1 characters
;
nprint:
2019-09-08 19:04:07 +02:00
mov ecx, ax1
jecxz .r
2019-08-21 18:47:03 +02:00
.l:
prn b[ax0]
inc ax0
loop .l
2019-09-03 09:06:58 +02:00
2019-08-21 18:47:03 +02:00
.r:
ret
# 51 "crt/crt.k" 2
exit:
2019-09-08 19:04:07 +02:00
mov eax, Sys.Exit
2019-08-21 18:47:03 +02:00
trap 0
abort:
crash
# 8 "doskrnl.k" 2
;
; Special addresses
;
; The kernel guarantees that COMMAND.COM
; will always be loaded on a 16KB boundary
; This is guaranteed to be the case in all
; future versions as well
;
; Entry point
;
start:
2019-09-07 16:45:03 +02:00
mov esp, 0x104000
mov ebp, zero
2019-08-21 18:47:03 +02:00
;dump
call main
crash
;
; Disk Operating System
;
# 1 "sys/drv/cpudev.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
CPUDEV := 0
RFS.GetMaxIdx:
iocall CPUDEV, 16
ret
RFS.GetUsage:
iocall CPUDEV, 17
ret
RFS.GetCurIdx:
iocall CPUDEV, 18
ret
RFS.GetLeastAvail:
iocall CPUDEV, 19
ret
RFS.IsFrameActive:
iocall CPUDEV, 20
ret
RFS.ActivateFrame:
iocall CPUDEV, 21
ret
RFS.DeactivateFrame:
iocall CPUDEV, 22
ret
RFS.CopyFrame:
iocall CPUDEV, 23
ret
RFS.MoveFrame:
iocall CPUDEV, 24
ret
RFS.SwitchFrame:
iocall CPUDEV, 25
ret
RFS.LoadArgs:
iocall CPUDEV, 32
ret
RFS.LoadReg:
iocall CPUDEV, 33
ret
RFS.StoreReg:
iocall CPUDEV, 48
ret
IDT.AddHandler:
iocall CPUDEV, 64
ret
IDT.DelHandler:
iocall CPUDEV, 65
ret
IDT.QueryHandler:
iocall CPUDEV, 66
IDT.DoneHandling:
iocall CPUDEV, 67
ret
# 43 "doskrnl.k" 2
# 1 "sys/drv/memdev.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
MEMDEV := 1
MEM.GetMemOff:
iocall MEMDEV, 0
ret
MEM.GetMemSize:
iocall MEMDEV, 1
ret
# 44 "doskrnl.k" 2
# 1 "sys/drv/diskdev.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
DISKDEV := 4
DISK.FindFirst:
.slot := 16
iocall DISKDEV, .slot
ret
DISK.FindNext:
.slot := 17
iocall DISKDEV, .slot
ret
DISK.OpenFile:
iocall DISKDEV, 25
ret
DISK.CloseFile:
iocall DISKDEV, 26
ret
DISK.CreateFile:
iocall DISKDEV, 27
ret
DISK.RemoveFile:
iocall DISKDEV, 28
ret
DISK.ReadFile:
iocall DISKDEV, 32
ret
DISK.WriteFile:
iocall DISKDEV, 33
ret
# 45 "doskrnl.k" 2
# 1 "sys/intr/excepts.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
DefaultExceptionHandler:
2019-09-07 16:45:03 +02:00
mov esp, 0x105800
mov ax1, nx0
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
bzr nx0, DefaultTrapHandler.handle_Shutdown
bltu 11, nx0, .unknown
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
mov esi, .err_ukn
lea ax0, b[esi + nx0 * 32]
2019-08-21 18:47:03 +02:00
call ScreenOfDeath
.unknown:
call ScreenOfDeath, .err_ukn
crash
.err_ukn = "Unknown exception number\0\0\0\0\0\0\0"
.err_udf = "Undefined behaviour exception\0\0"
.err_ill = "Illformed instruction exception"
.err_acc = "Invalid memory access exception"
.err_sys = "Supervisor-only instruct. used\0"
.err_dbf = "Double fault exception~~~~~~~~\0"
.err_imp = "Feat. not implemented exception"
.err_ali = "Misalignmed address exception\0\0"
.err_brk = "BREAK key (Ctrl+C) exception\0\0\0"
.err_ovf = "Overflow flag raised exception\0"
.err_div = "Division by zero exception\0\0\0\0\0"
# 47 "doskrnl.k" 2
# 1 "sys/intr/common.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
ScreenOfDeath:
2019-09-07 16:45:03 +02:00
push nx0
mov nx0, ax0
2019-08-21 18:47:03 +02:00
prn 10
prn 0x8BF00001
prn 0x8BF00002
push ax1
call printf, .scr1
pop
2019-09-07 16:45:03 +02:00
bnz nx2, .not_con
2019-08-21 18:47:03 +02:00
push .scr2_con
jmp .do_print
.not_con:
; rframes #2-#3-#4 belong to kernel
2019-09-07 16:45:03 +02:00
blt 4, nx2, .not_krn
2019-08-21 18:47:03 +02:00
push .scr2_krn
jmp .do_print
.not_krn:
push .scr2_usr
.do_print:
2019-09-07 16:45:03 +02:00
push nx2, nx0
2019-08-21 18:47:03 +02:00
call printf, .scr2
2019-09-07 16:45:03 +02:00
add esp, 24
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
call dumprf, nx2
2019-08-21 18:47:03 +02:00
prn 10
call print, .scr3
.loop:
pause
pause
2019-09-07 16:45:03 +02:00
scan eax
2019-09-08 19:04:07 +02:00
jeaxz .loop
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
beq eax, 0x0A, DefaultTrapHandler.handle_Exit
beq eax, 0x1B, DefaultTrapHandler.handle_Shutdown
2019-08-21 18:47:03 +02:00
jmp .loop
crash
.scr1 = "-------- Unhandled EXCEPTION, TRAP/SYSCALL or INTERRUPT (code %d)\n"
.scr2 = "Description: %s\nFrame: %d (%s)\n\n"
.scr3 = "Press:\n ENTER to procede to COMMAND.COM\n ESCAPE to shutdown machine\n\n"
.scr2_con = "dedicated command.com frame"
.scr2_usr = "userspace application frame"
.scr2_krn = "exception/interrupt handler"
# 48 "doskrnl.k" 2
# 1 "sys/intr/trap0.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
TrapHandlers.prolog:
2019-09-07 16:45:03 +02:00
nul ebp
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
; eax = caller's cr1
call RFS.LoadReg, nx2, $cr1
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
; we don't preserve the nx0 we got
mov nx0, eax
nul edx
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
jmp ecx
2019-08-21 18:47:03 +02:00
TrapHandlers.epilog:
2019-09-07 16:45:03 +02:00
; TRAP return values: eax-edx
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
mov ax2, eax
call RFS.StoreReg, nx2, $eax
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
mov ax2, edx
call RFS.StoreReg, nx2, $edx
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
call IDT.DoneHandling, nx1
2019-08-21 18:47:03 +02:00
iret
;
; TRAP #0 handler
;
DefaultTrapHandler:
.init:
2019-09-07 16:45:03 +02:00
mov ecx, .text
mov esp, 0x104800
2019-08-21 18:47:03 +02:00
jmp TrapHandlers.prolog
.text:
2019-09-07 16:45:03 +02:00
call RFS.LoadReg, nx2, $eax
call RFS.LoadArgs, nx2
2019-08-21 18:47:03 +02:00
; will be optimized with a table
; when we have a "finished" consistent API
2019-09-08 19:04:07 +02:00
jeaxz .handle_Shutdown
2019-09-07 16:45:03 +02:00
beq eax, Sys.Exit, .handle_Exit
beq eax, Sys.ExecuteInFrame, .handle_EIF
beq eax, Sys.ReadFile, .handle_ReadFile
beq eax, Sys.OpenFile, .handle_OpenFile
beq eax, Sys.CloseFile, .handle_CloseFile
beq eax, Sys.CreateFile, .handle_CreateFile
beq eax, Sys.RemoveFile, .handle_RemoveFile
beq eax, Sys.FindNext, .handle_FindNext
beq eax, Sys.FindFirst, .handle_FindFirst
beq eax, Sys.EnterHaltMode, .handle_HaltMode
2019-08-21 18:47:03 +02:00
.sod:
2019-09-07 16:45:03 +02:00
call ScreenOfDeath, .badsyscall, nx1
2019-08-21 18:47:03 +02:00
.wrong:
2019-09-07 16:45:03 +02:00
call ScreenOfDeath, .badparams, nx1
2019-08-21 18:47:03 +02:00
.fini.savecx:
2019-09-07 16:45:03 +02:00
mov ax2, ecx
call RFS.StoreReg, nx2, $ecx
2019-08-21 18:47:03 +02:00
.fini:
jmp TrapHandlers.epilog
2019-09-07 16:45:03 +02:00
.badsyscall = "Invalid syscall number in eax"
2019-08-21 18:47:03 +02:00
.badparams = "Invalid syscall parameters"
;------------------------------------------------;
; Syscall implementations ;
;------------------------------------------------;
;
; Pass control to COMMAND.COM in frame 0
;
.handle_Exit:
; deactivate current rframe
2019-09-07 16:45:03 +02:00
bzr nx2, .hE_nz ; unless it's 0...
call RFS.DeactivateFrame, nx2
2019-08-21 18:47:03 +02:00
.hE_nz:
; open COMMAND.COM
call DISK.OpenFile, .cmdcom
; crash on failure
2019-09-07 16:45:03 +02:00
bltz eax, abort
2019-08-21 18:47:03 +02:00
2019-09-03 09:06:58 +02:00
; load at 0x108000
mov ax1, 0x108000
mov ax2, 0x80000
2019-09-07 16:45:03 +02:00
call DISK.ReadFile, eax
2019-08-21 18:47:03 +02:00
; assume that COMMAND.COM being
; less then 4KB means something
; went wrong
2019-09-07 16:45:03 +02:00
blt eax, 0x1000, abort
2019-08-21 18:47:03 +02:00
; close the handle
2019-09-07 16:45:03 +02:00
call DISK.CloseFile, eax
2019-08-21 18:47:03 +02:00
; code address
mov ax2, 0x100000
2019-09-07 16:45:03 +02:00
call RFS.StoreReg, zero, $eip
2019-08-21 18:47:03 +02:00
; usermode
mov ax2, 3
call RFS.StoreReg, zero, $cr0
2019-09-07 16:45:03 +02:00
mov ecx, 0x108000
sub ecx, 0x100000
2019-08-21 18:47:03 +02:00
; code offset
2019-09-07 16:45:03 +02:00
mov ax2, ecx
2019-08-21 18:47:03 +02:00
call RFS.StoreReg, zero, $cr1
; return frame
2019-09-07 16:45:03 +02:00
nul nx2
2019-08-21 18:47:03 +02:00
jmp .fini
.cmdcom = "command.com"
.handle_EIF:
blt ax1, 5, .wrong
2019-09-07 16:45:03 +02:00
; eip can't be <1MB
mov ecx, 0x100000
blt ax0, ecx, .wrong
2019-08-21 18:47:03 +02:00
; add old CR1
2019-09-07 16:45:03 +02:00
add ax0, nx0
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
; real eip can't be <1MB+32KB either
2019-08-21 18:47:03 +02:00
; (kernel + cmdcom personal space)
2019-09-07 16:45:03 +02:00
add edx, ecx, 0x8000
blt ax0, ecx, .wrong
2019-08-21 18:47:03 +02:00
; save rframe and compute new CR1
2019-09-07 16:45:03 +02:00
mov ebx, ax1
sub edi, ax0, ecx
2019-08-21 18:47:03 +02:00
; activate rframe
2019-09-07 16:45:03 +02:00
call RFS.ActivateFrame, ebx
2019-08-21 18:47:03 +02:00
; save new CR1
2019-09-07 16:45:03 +02:00
mov ax2, edi
call RFS.StoreReg, ebx, $cr1
2019-08-21 18:47:03 +02:00
; interruptible user mode
mov ax2, 3
2019-09-07 16:45:03 +02:00
call RFS.StoreReg, ebx, $cr0
2019-08-21 18:47:03 +02:00
2019-09-07 16:45:03 +02:00
; set eip = 1MB
mov ax2, ecx
call RFS.StoreReg, ebx, $eip
2019-08-21 18:47:03 +02:00
; change return frame
2019-09-07 16:45:03 +02:00
mov nx2, ebx
2019-08-21 18:47:03 +02:00
jmp .fini
;
; Disk API
;
.handle_FindFirst:
2019-09-07 16:45:03 +02:00
add ax0, nx0
2019-08-21 18:47:03 +02:00
call DISK.FindFirst
jmp .fini.savecx
.handle_FindNext:
2019-09-07 16:45:03 +02:00
add ax0, nx0
2019-08-21 18:47:03 +02:00
call DISK.FindNext
jmp .fini.savecx
.handle_OpenFile:
2019-09-07 16:45:03 +02:00
add ax0, nx0
2019-08-21 18:47:03 +02:00
call DISK.OpenFile
jmp .fini
.handle_CreateFile:
2019-09-07 16:45:03 +02:00
add ax0, nx0
2019-08-21 18:47:03 +02:00
call DISK.CreateFile
jmp .fini
.handle_RemoveFile:
2019-09-07 16:45:03 +02:00
add ax0, nx0
2019-08-21 18:47:03 +02:00
call DISK.RemoveFile
jmp .fini
.handle_CloseFile:
call DISK.CloseFile
jmp .fini
.handle_ReadFile:
2019-09-07 16:45:03 +02:00
add ax1, nx0
2019-08-21 18:47:03 +02:00
call DISK.ReadFile
jmp .fini
;
; Misc.
;
.handle_Shutdown:
call IDT.DelHandler, zero
stop
.handle_HaltMode:
pause
pause
hlt
jmp .handle_HaltMode
# 49 "doskrnl.k" 2
# 1 "sys/dumprf.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
dumprf:
2019-09-08 19:04:07 +02:00
push ebp
mov ebp, esp
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
push nx0, nx1
mov nx0, ax0
mov nx1, esp
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $cr1
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $cr0
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $eip
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
push nx0
2019-08-21 18:47:03 +02:00
call printf, .dmp1
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $srp
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $trp
push eax
2019-08-21 18:47:03 +02:00
push zero # fixme
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $ebp
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $esp
push eax
2019-08-21 18:47:03 +02:00
call printf, .dmp2
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $ax5
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $ax4
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $ax3
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $ax2
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $ax1
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $ax0
push eax
2019-08-21 18:47:03 +02:00
call printf, .dmp3
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $edi
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $esi
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $edx
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $ecx
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $ebx
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $eax
push eax
2019-08-21 18:47:03 +02:00
call printf, .dmp4
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $nx5
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $nx4
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $nx3
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $nx2
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $nx1
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $nx0
push eax
2019-08-21 18:47:03 +02:00
call printf, .dmp5
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $nx8
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $nx7
push eax
2019-08-21 18:47:03 +02:00
2019-09-08 19:04:07 +02:00
call RFS.LoadReg, nx0, $nx6
push eax
2019-08-21 18:47:03 +02:00
call printf, .dmp6
2019-09-08 19:04:07 +02:00
mov esp, nx1
pop nx1, nx0
2019-08-21 18:47:03 +02:00
leave
ret
2019-09-08 19:04:07 +02:00
.dmp1 = "Environ #1:\nfrm=0d%d eip=0x%x\ncr0=0x%x cr1=0x%x\n\n"
.dmp2 = "Environ #2:\nesp=0x%x ebp=0x%x ind=0d%d\ngrp=0x%x trp=0x%x srp=0x%x\n\n"
2019-08-21 18:47:03 +02:00
.dmp3 = "Argument:\nax0=0x%x ax1=0x%x ax2=0x%x\nax3=0x%x ax4=0x%x ax5=0x%x\n\n"
2019-09-08 19:04:07 +02:00
.dmp4 = "Volatile:\neax=0x%x ebx=0x%x ecx=0x%x\nrdx=0x%x esi=0x%x edi=0x%x\n\n"
.dmp5 = "Persistent:\nnx0=0x%x nx1=0x%x nx2=0x%x\nr15=0x%x nx4=0x%x nx5=0x%x\n"
.dmp6 = "nx6=0x%x nx7=0x%x nx8=0x%x\n"
2019-08-21 18:47:03 +02:00
# 51 "doskrnl.k" 2
# 1 "sys/main.k" 1
; The OS/K Team licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
PrintBootMsg:
call print, .bootmsg
ret
.bootmsg = "Starting DOS...\n\n"
;
; Initialize TRAP handlers
;
InitSyscalls:
call RFS.ActivateFrame, 1
2019-09-08 19:04:07 +02:00
mov ax1, $eip
2019-08-21 18:47:03 +02:00
mov ax2, DefaultTrapHandler
call RFS.StoreReg
mov ax0, 256
mov ax1, 1
call IDT.AddHandler
ret
InitExcepts:
call RFS.ActivateFrame, 2
2019-09-08 19:04:07 +02:00
mov ax1, $eip
2019-08-21 18:47:03 +02:00
mov ax2, DefaultExceptionHandler
call RFS.StoreReg
call IDT.AddHandler, zero, 2
SwitchToCMD:
2019-09-08 19:04:07 +02:00
mov eax, Sys.Exit
2019-08-21 18:47:03 +02:00
trap 0
;
; Main function
;
main:
call PrintBootMsg
call InitSyscalls
call InitExcepts
call SwitchToCMD
ret
# 52 "doskrnl.k" 2