# 1 "hello.k" # 1 "" # 1 "" # 31 "" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 32 "" 2 # 1 "hello.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. start: jmp main # 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: mov ecx, ax1 scasb ax0, zero sub eax, ax1, ecx ret ; ; int strlen(char *) ; strlen: mov ecx, 0x7AFFFFFF mov edx, ecx scasb ax0, zero sub eax, edx, ecx ret ; ; void strcpy(char *, const char *) ; strcpy: .l: mov ecx, b[ax1] mov b[ax0], ecx jecxz .r inc ax0 inc ax1 jmp .l .r: ret ; ; void strncpy(char *, const char *, int) ; strncpy: mov ecx, ax2 jecxz .r .l: mov b[ax0], b[ax1] inc ax0 inc ax1 loop .l .r: ret ; ; void strnzcpy(char *, const char *, int) ; strnzcpy: mov ecx, ax2 jecxz .r .l: mov eax, b[ax1] mov b[ax0], eax jeaxz .r 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: nul esi .l: movzx eax, b[ax0+esi] movzx edx, b[ax1+esi] bne eax, edx, .r ; both zero? add ecx, eax, edx jecxz .r inc esi jmp .l .r: sub eax, edx ret ; ; int strncmp(const char *str1, const char *str2, int maxn) ; strncmp: mov ecx, ax2 jecxz .r .l: movzx eax, b[ax0] movzx edx, b[ax1] bne eax, edx, .r inc ax0 inc ax1 loop .l .r: sub eax, edx ret ; ; char *strchrnul(const char *str, int ch) ; strchrnul: mov ecx, 0x7AFFFFFF scasb ax0, ax1 mov eax, ax0 ret ; ; char *strchr(const char *str, int ch) ; strchr: mov ecx, 0x7AFFFFFF scasb ax0, ax1 bnz b[ax0], .r nul eax ret .r: mov eax, ax0 ret ; ; void strrev(char *buf, const char *str) ; ; buf and src must NOT overlap ; strrev: bzr b[ax1], .z ; save str's location mov esi, ax1 ; go to str's end, just before ; the null terminator mov ecx, 0x7AFFFFFF scasb ax1, zero dec ax1 .l: ; copy, going backward though str ; and forward through buf mov b[ax0], b[ax1] beq ax1, esi, .r 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 mov ecx, 0x7AFFFFFF 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: mov ecx, ax2 jecxz .r .l: sub edx, ax2, ecx mov b[ax0+edx], b[ax1+edx] loop .l .r: ret ; ; void memzero(void *, int) ; memzero: mov ecx, ax1 jecxz .r .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: mov eax, 365 ; divisible by 4? rem ecx, ax0, 4 jecxnz .end ; divisible by 100? rem ecx, ax0, 100 jecxnz .leap ; divisible by 400? rem ecx, ax0, 400 jecxnz .end .leap: inc eax .end: ret ; ; TIME *GetTimeUTC(void) ; GetTimeUTC: ytime mov edi, .buf ; seconds rem esi, eax, 60 mov b[edi], esi ; minutes div esi, eax, 60 rem esi, 60 mov b[edi+1], esi ; hours div esi, eax, 3600 rem esi, 24 mov b[edi+2], esi ; month days div esi, eax, 3600*24 mov b[edi+3], esi ; month mov b[edi+4], rbx ; years mov w[edi+6], ecx ; ; ydays (TODO) ; mov eax, .buf 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: mov eax, ax0 nul ecx ; 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 shr ecx, ax1, 63 ; extract ax1 sign jecxz .conv neg ax1 ; NEG if negative ; main loop .conv: bzr ax1, .fini rem edx, ax1, ax2 ; ax1 % base blt 9, edx, .nondec ; edx > 9 ? add edx, '0' jmp .next .nondec: add edx, 55 ; 'A' - 10 .next: mov b[ax0], edx inc ax0 div ax1, ax2 jmp .conv ; add minus flag, null-terminate and reverse .fini: jecxz .cxz mov b[ax0], '-' inc ax0 .cxz: nul b[ax0] call strrev2, eax 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) ; ; eax = integer extracted from str ; edx = pointer to first invalid byte ; strtol: mov ax2, 1 jmp strtoq ; ; int strtoul(const char *str, int base) ; ; eax = integer extracted from str ; edx = pointer to first invalid byte ; strtoul: nul ax2 jmp strtoq ; ; int strtoq(const char *str, int base, bool signed) ; ; guesses base when 'base'=0 ; strtoq: nul eax, esi mov edx, ax0 ; make sure base is in [2, 32] beq ax1, 1, .bad bltu 36, ax1, .bad ; empty string? bzr b[edx], .done .skip_spc: bne b[edx], ' ', .no_spc inc edx jmp .skip_spc .no_spc: ; skip + bne b[edx], '+', .no_plus inc edx .no_plus: ; unsigned? bzr ax2, .unsigned ; parse '-' bne b[edx], '-', .unsigned inc edx mov esi, 1 .unsigned: ; base 0 bzr ax1, .base_0 ; base prefix? bne b[edx], '0', .main_loop inc edx movzx ecx, b[edx] ; "0x"/"0b" prefix jecxz .done ; "0" beq ecx, 'x', .parsed_0x beq ecx, 'b', .parsed_0b ; 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? ; if not, leave eax = 0 and *edx = 'x' bne ax1, 16, .done ; else inc edx jmp .main_loop .parsed_0b: ; are we in base 2? ; if not, leave eax = 0 and *edx = 'b' bne ax1, 2, .done ; else inc edx jmp .main_loop .base_0: ; guess base beq b[edx], '0', .b0_not10 ; must be base 10 mov ax1, 10 jmp .main_loop .b0_not10: inc edx bne b[edx], 'x', .b0_not16 inc edx mov ax1, 16 jmp .main_loop .b0_not16: bne b[edx], 'b', .b0_not2 inc edx mov ax1, 2 jmp .main_loop .b0_not2: ; take octal by default mov ax1, 8 .main_loop: movzx ecx, b[edx] inc edx ; between 0 and 9? bltu ecx, '0', .done bltu '9', ecx, .not_digit10 ; yes sub ecx, '0' jmp .next .not_digit10: bltu ecx, 'A', .done bltu 'Z', ecx, .not_digitAZ sub ecx, 55 ; 'A' - 10 jmp .next .not_digitAZ: bltu ecx, 'a', .done bltu 'z', ecx, .done sub ecx, 87 ; 'a' - 10 jmp .next .next: ; too large for base? blteu ax1, ecx, .done mul eax, ax1 add eax, ecx jmp .main_loop .done: ; negative? bzr esi, .r ; yes neg eax .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: push ebp mov ebp, esp push nx0, nx1, nx2 push nx3, nx4, nx5 mov nx0, ax2 ; fmt mov nx2, ax3 ; va_list mov nx3, ax1 ; n mov nx5, ax0 ; putc nul nx4 ; return value .main_loop: ; find '%' or null-terminator mov ecx, 0x7AFFFFFF mov nx1, nx0 scasb nx1, '%' ; everything below nx1 is a regular character; print it .print_regular: beq nx0, nx1, .check_modf call .doput, b[nx0] inc nx0 jmp .print_regular .check_modf: ; did we find a '%' ? ; if not, then we found fmt's null-terminator; we're done bne b[nx0], '%', .epilogue ; we did find a modifier / '%' 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 ; unrecognized jmp .bad_modifier .modf_s: ; get string address mov nx1, q[nx2] add nx2, 8 bzr nx1, .nullstring .print_string: movzx ax0, b[nx1] bzr ax0, .main_loop inc nx1 call .doput jmp .print_string .modf_c: call .doput, q[nx2] add nx2, 8 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 sub esp, 80 mov nx1, esp ; assume modifier already set up ax2 call itoa, esp, q[nx2] add nx2, 8 .print_itoa_buf: movzx ax0, b[nx1] bzr ax0, .pib_end_loop inc nx1 call .doput jmp .print_itoa_buf .pib_end_loop: add esp, 80 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: mov eax, nx4 pop nx5, nx4 pop nx3, nx2 pop nx1, nx0 leave ret ; ; prints ax0 ; .doput: ; update print count inc nx4 ; 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 bzr nx3, .r ; decrement n and print dec nx3 call nx5 ; did putc fail? jeaxz .r ; yes, so artificially set n=0 nul nx3 .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 nul eax ret ; ; int printf(const char *fmt, ...) ; printf: mov ax2, ax0 mov ax0, putc mov ax1, 0x7AFFFFFF add ax3, esp, 8 jmp doprnt ; ; int nprintf(const char *fmt, int n, ...) ; nprintf: mov ax2, ax0 mov ax0, putc add ax3, esp, 8 jmp doprnt ; ; Print a string ; Guaranteed to only affect ecx and ax0 ; print: .l: movzx eax, b[ax0] jeaxz .r prn eax inc ax0 jmp .l .r: ret ; ; Print exactly ax1 characters ; nprint: mov ecx, ax1 jecxz .r .l: prn b[ax0] inc ax0 loop .l .r: ret # 51 "crt/crt.k" 2 exit: mov eax, Sys.Exit trap 0 abort: crash # 8 "hello.k" 2 main: mov esp, 0x104000 nul ebp call print, .hellow jmp exit .hellow = "Hello World!\n"