# 1 "command.k" # 1 "" # 1 "" # 31 "" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 32 "" 2 # 1 "command.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. cmdstart: 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: mov rcx, ax1 scasb ax0, zero sub rax, ax1, rcx ret ; ; int strlen(char *) ; strlen: mov rcx, 0x7AFFFFFF mov rdx, rcx scasb ax0, zero sub rax, rdx, rcx ret ; ; void strcpy(char *, const char *) ; strcpy: .l: mov rcx, b[ax1] mov b[ax0], rcx jrcxz .r inc ax0 inc ax1 jmp .l .r: ret ; ; void strncpy(char *, const char *, int) ; strncpy: mov rcx, ax2 jrcxz .r .l: mov b[ax0], b[ax1] inc ax0 inc ax1 loop .l .r: ret ; ; void strnzcpy(char *, const char *, int) ; strnzcpy: mov rcx, ax2 jrcxz .r .l: mov rax, b[ax1] mov b[ax0], rax jraxz .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 rsi .l: movzx rax, b[ax0+rsi] movzx rdx, b[ax1+rsi] bne rax, rdx, .r ; both zero? add rcx, rax, rdx jrcxz .r inc rsi jmp .l .r: sub rax, rdx ret ; ; int strncmp(const char *str1, const char *str2, int maxn) ; strncmp: mov rcx, ax2 jrcxz .r .l: movzx rax, b[ax0] movzx rdx, b[ax1] bne rax, rdx, .r inc ax0 inc ax1 loop .l .r: sub rax, rdx ret ; ; char *strchrnul(const char *str, int ch) ; strchrnul: mov rcx, 0x7AFFFFFF scasb ax0, ax1 mov rax, ax0 ret ; ; char *strchr(const char *str, int ch) ; strchr: mov rcx, 0x7AFFFFFF scasb ax0, ax1 bnz b[ax0], .r nul rax ret .r: mov rax, 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 rsi, ax1 ; go to str's end, just before ; the null terminator mov rcx, 0x7AFFFFFF scasb ax1, zero dec ax1 .l: ; copy, going backward though str ; and forward through buf mov b[ax0], b[ax1] beq ax1, rsi, .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 rcx, 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 rcx, ax2 jrcxz .r .l: sub rdx, ax2, rcx mov b[ax0+rdx], b[ax1+rdx] loop .l .r: ret ; ; void memzero(void *, int) ; memzero: mov rcx, ax1 jrcxz .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 rax, 365 ; divisible by 4? rem rcx, ax0, 4 jrcxnz .end ; divisible by 100? rem rcx, ax0, 100 jrcxnz .leap ; divisible by 400? rem rcx, ax0, 400 jrcxnz .end .leap: inc rax, 1 .end: ret ; ; TIME *GetTimeUTC(void) ; GetTimeUTC: ytime mov rdi, .buf ; seconds rem rsi, rax, 60 mov b[rdi], rsi ; minutes div rsi, rax, 60 rem rsi, 60 mov b[rdi+1], rsi ; hours div rsi, rax, 3600 rem rsi, 24 mov b[rdi+2], rsi ; month days div rsi, rax, 3600*24 mov b[rdi+3], rsi ; month mov b[rdi+4], rbx ; years mov w[rdi+6], rcx ; ; ydays (TODO) ; mov rax, .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 rax, ax0 nul rcx ; 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 rcx, ax1, 63 ; extract ax1 sign jrcxz .conv neg ax1 ; NEG if negative ; main loop .conv: bzr ax1, .fini rem rdx, ax1, ax2 ; ax1 % base blt 9, rdx, .nondec ; rdx > 9 ? inc rdx, '0' jmp .next .nondec: inc rdx, 55 ; 'A' - 10 .next: mov b[ax0], rdx inc ax0 div ax1, ax2 jmp .conv ; add minus flag, null-terminate and reverse .fini: jrcxz .cxz mov b[ax0], '-' inc ax0 .cxz: nul b[ax0] call strrev2, rax 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) ; ; rax = integer extracted from str ; rdx = pointer to first invalid byte ; strtol: mov ax2, 1 jmp strtoq ; ; int strtoul(const char *str, int base) ; ; rax = integer extracted from str ; rdx = 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 rax, rsi mov rdx, ax0 ; make sure base is in [2, 32] beq ax1, 1, .bad bltu 36, ax1, .bad ; empty string? bzr b[rdx], .done .skip_spc: bne b[rdx], ' ', .no_spc inc rdx jmp .skip_spc .no_spc: ; skip + bne b[rdx], '+', .no_plus inc rdx .no_plus: ; unsigned? bzr ax2, .unsigned ; parse '-' bne b[rdx], '-', .unsigned inc rdx mov rsi, 1 .unsigned: ; base 0 bzr ax1, .base_0 ; base prefix? bne b[rdx], '0', .main_loop inc rdx movzx rcx, b[rdx] ; "0x"/"0b" prefix jrcxz .done ; "0" beq rcx, 'x', .parsed_0x beq rcx, '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 rax = 0 and *rdx = 'x' bne ax1, 16, .done ; else inc rdx, 1 jmp .main_loop .parsed_0b: ; are we in base 2? ; if not, leave rax = 0 and *rdx = 'b' bne ax1, 2, .done ; else inc rdx jmp .main_loop .base_0: ; guess base beq b[rdx], '0', .b0_not10 ; must be base 10 mov ax1, 10 jmp .main_loop .b0_not10: inc rdx bne b[rdx], 'x', .b0_not16 inc rdx mov ax1, 16 jmp .main_loop .b0_not16: bne b[rdx], 'b', .b0_not2 inc rdx mov ax1, 2 jmp .main_loop .b0_not2: ; take octal by default mov ax1, 8 .main_loop: movzx rcx, b[rdx] inc rdx ; between 0 and 9? bltu rcx, '0', .done bltu '9', rcx, .not_digit10 ; yes sub rcx, '0' jmp .next .not_digit10: bltu rcx, 'A', .done bltu 'Z', rcx, .not_digitAZ sub rcx, 55 ; 'A' - 10 jmp .next .not_digitAZ: bltu rcx, 'a', .done bltu 'z', rcx, .done sub rcx, 87 ; 'a' - 10 jmp .next .next: ; too large for base? blteu ax1, rcx, .done mul rax, ax1 inc rax, rcx jmp .main_loop .done: ; negative? bzr rsi, .r ; yes neg rax .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 rbp mov rbp, rsp push r12, r13, r14 push r15, r16, r17 mov r12, ax2 ; fmt mov r14, ax3 ; va_list mov r15, ax1 ; n mov r17, ax0 ; putc nul r16 ; return value .main_loop: ; find '%' or null-terminator mov rcx, 0x7AFFFFFF mov r13, r12 scasb r13, '%' ; everything below r13 is a regular character; print it .print_regular: beq r12, r13, .check_modf call .doput, b[r12] inc r12, 1 jmp .print_regular .check_modf: ; did we find a '%' ? ; if not, then we found fmt's null-terminator; we're done bne b[r12], '%', .epilogue ; we did find a modifier / '%' mov rax, b[r12+1] inc r12, 2 beq rax, 's', .modf_s beq rax, 'c', .modf_c beq rax, 'p', .modf_p beq rax, 'x', .modf_x beq rax, 'd', .modf_d beq rax, 'o', .modf_o beq rax, 'b', .modf_b beq rax, '%', .modf_percent ; unrecognized jmp .bad_modifier .modf_s: ; get string address mov r13, q[r14] inc r14, 8 bzr r13, .nullstring .print_string: movzx ax0, b[r13] bzr ax0, .main_loop inc r13 call .doput jmp .print_string .modf_c: call .doput, q[r14] inc r14, 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 rsp, 80 mov r13, rsp ; assume modifier already set up ax2 call itoa, rsp, q[r14] inc r14, 8 .print_itoa_buf: movzx ax0, b[r13] bzr ax0, .pib_end_loop inc r13 call .doput jmp .print_itoa_buf .pib_end_loop: inc rsp, 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 rax, r16 pop r17, r16 pop r15, r14 pop r13, r12 leave ret ; ; prints ax0 ; .doput: ; update print count inc r16 ; 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 r15, .r ; decrement n and print dec r15 call r17 ; did putc fail? jraxz .r ; yes, so artificially set n=0 nul r15 .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 rax ret ; ; int printf(const char *fmt, ...) ; printf: mov ax2, ax0 mov ax0, putc mov ax1, 0x7AFFFFFF add ax3, rsp, 8 jmp doprnt ; ; int nprintf(const char *fmt, int n, ...) ; nprintf: mov ax2, ax0 mov ax0, putc add ax3, rsp, 8 jmp doprnt ; ; Print a string ; Guaranteed to only affect rcx and ax0 ; print: .l: movzx rax, b[ax0] jraxz .r prn rax inc ax0 jmp .l .r: ret ; ; Print exactly ax1 characters ; nprint: mov rcx, ax1 jrcxz .r .l: prn b[ax0] inc ax0 loop .l .r: ret # 51 "crt/crt.k" 2 exit: mov rax, Sys.Exit trap 0 abort: crash # 8 "command.k" 2 ; COMMAND.COM guarantees that programs ; are always loaded on a 16KB boundary ; This is guaranteed to be the case ; in all future versions as well start: mov rsp, 0x104000 ; + 16KB mov rbp, zero call main mov rax, Sys.EnterHaltMode trap 0 crash cmd.versionstr = "COMMAND.COM, version 0.1 (KVISC)\nCopyright (C) 2019, The OS/K Team\nMIT license (permissive), see LICENCE file in source tree" argbuf.size := 256 argbuf = [argbuf.size] argv0 = [argbuf.size] argv1pos = 0 stdin_echoing = 1 prompt = [32] main: mov rsi, prompt mov b[rsi+0], 'C' mov b[rsi+1], ':' mov b[rsi+2], '\' mov b[rsi+3], '>' mov b[rsi+4], ' ' .print_prompt: call print, prompt ; empty stuff call memzero, argbuf, argbuf.size call memzero, argv0, argbuf.size nul q[argv1pos] ; call nprint, argv0, argbuf.size ; iterator through argbuf nul rcx .input_loop: pause pause pause ; Fill .buf with user input scan rax jraxz .input_loop ; ESC key pressed? beq rax, 0x1B, .handle_EXIT ; backspace character? bne rax, 8, .handle_input ; anything to delete? jrcxz .input_loop ; no ; yes, delete it dec rcx add rdx, rcx, argbuf nul b[rdx] ; update screen bzr b[stdin_echoing], .input_loop prn 8 jmp .input_loop .handle_input: bzr b[stdin_echoing], .se.z prn rax .se.z: beq rax, 10, .extract_argv0 ; when max line length is reached, ; force a newline beq rcx, argbuf.size, .extract_argv0 ; add character to buffer and increase iterator (rcx) add rdx, rcx, argbuf mov b[rdx], rax inc rcx ; another one jmp .input_loop .extract_argv0: ; did we read anything at all? ; if not, just go back to waiting input jrcxz .print_prompt ; find first whitespace or null-terminator mov rcx, argbuf.size mov rdx, argbuf scasb rdx, ' ' ; argv1 exists? if so, save its position mov rsi, rdx .next_space: mov rcx, b[rsi] jrcxz .do_extract ; skip spaces bne rcx, ' ', .not_a_space inc rsi jmp .next_space .not_a_space: ; if we're here, we found a ; non-zero non-space character mov q[argv1pos], rsi ; fallthrough .do_extract: ; how much do we copy? sub rcx, rdx, argbuf jrcxz .detect_builtin dec rcx mov rdi, argbuf mov rsi, argv0 .copy_loop: mov b[rsi], b[rdi] inc rdi inc rsi loop .copy_loop .detect_builtin: .builtin_cls = "cls" call strcmp, argv0, .builtin_cls jraxz .handle_CLS .builtin_crash = "crash" call strcmp, argv0, .builtin_crash jraxz .handle_CRASH .builtin_date = "date" call strcmp, argv0, .builtin_date jraxz .handle_DATE .builtin_dir = "dir" call strcmp, argv0, .builtin_dir jraxz .handle_DIR .builtin_dump = "dump" call strcmp, argv0, .builtin_dump jraxz .handle_DUMP .builtin_echo = "echo" call strcmp, argv0, .builtin_echo jraxz .handle_ECHO .builtin_erase = "erase" call strcmp, argv0, .builtin_erase jraxz .handle_ERASE .builtin_exit = "exit" call strcmp, argv0, .builtin_exit jraxz .handle_EXIT .builtin_help = "help" call strcmp, argv0, .builtin_help jraxz .handle_HELP .builtin_halt = "halt" call strcmp, argv0, .builtin_halt jraxz .handle_HALT .builtin_make = "make" call strcmp, argv0, .builtin_make jraxz .handle_MAKE .builtin_print = "print" call strcmp, argv0, .builtin_print jraxz .handle_PRINT .builtin_prompt = "prompt" call strcmp, argv0, .builtin_prompt jraxz .handle_PROMPT .builtin_remove = "remove" call strcmp, argv0, .builtin_remove jraxz .handle_REMOVE .builtin_time = "time" call strcmp, argv0, .builtin_time jraxz .handle_TIME .builtin_vers = "vers" call strcmp, argv0, .builtin_vers jraxz .handle_VERS jmp .try_exec ; ; call builtins ; # 1 "usr/cmd-dir.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. N := 11 .handle_DIR: push rbp mov rbp, rsp push r12, r13 push r14, r15 nul r12 ; no. of files found nul r13 ; no. of directories found nul r14 ; total amount of bytes found call print, .dirmsg .first: mov rax, Sys.FindFirst mov ax0, .buf mov ax1, 0x80 trap 0 jmp .list .next: mov rax, Sys.FindNext mov ax0, .buf mov ax1, 0x80 trap 0 .list: jraxz .end mov r15, rcx ; file size inc r14, rcx ; directory? bnz rdx, .is_dir ; found a file inc r12 ; separate extension from file name mov rcx, 0x80 mov rsi, .buf mov rdi, rsi scasb rsi, '.' ; print file name sub ax1, rsi, rdi dec ax1 call nprint, rdi ; calculate where to put extension sub rdi, rsi, .buf dec rdi .ext_pad: ; print at least N non-space characters before extension blte N, rdi, .print_ext prn ' ' inc rdi jmp .ext_pad .print_ext: prn ' ' ; here we print at least 4 characters excluding '.' mov rcx, 4 bne b[rsi], '.', .print_ext.1 inc rsi .print_ext.1: bzr b[rsi], .print_ext.2 ; print and decrease rcx, unless it's already 0 prn b[rsi] inc rsi jrcxz .print_ext.1 dec rcx jmp .print_ext.1 .print_ext.2: ; did we print at least 4 bytes? jrcxz .print_bytes ; yes, carry on .pe2.l: prn ' ' loop .pe2.l .print_bytes: ; print file size in bytes prn ' ' prn ' ' prn ' ' shr rax, r15, 10 and r15, 1023 push r15, rax call printf, .bytesstr inc rsp, 16 .prepare_next: ; go find next entry prn 10 jmp .next .end: shr rax, r14, 10 shr rdx, rax, 10 and rax, 1023 and r14, 1023 push r14, rax, rdx call printf, .endstr0 inc rsp, 24 push r13, r12 call printf, .endstr1 inc rsp, 16 pop r15, r14 pop r13, r12 leave jmp .print_prompt ; special case: direcory .is_dir: inc r13 ; use printf instead of print ; because it returns # of printed ; characters call printf, .buf blte N, rax, .dir_no_pad sub rcx, N, rax dec rcx .dir.l: prn ' ' loop .dir.l .dir_no_pad: call print, .dir_ext jmp .print_bytes .buf = [0x80] .dir_ext = " " .endstr0 = " total %dMB + %dKB + %dB\n" .endstr1 = " found %d file(s), %d dir(s)\n" .dirmsg = "Directory of C:\\\n\n" .bytesstr = "%d kilobytes + %d bytes" ; .bytesstr = "%dMB + %dKB + %dB" # too soon # 228 "command.k" 2 # 1 "usr/cmd-exec.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. .try_exec: ; try without appending ".com" mov rax, Sys.OpenFile mov ax0, argv0 trap 0 ; we good? blte zero, rax, .do_exec ; nope, append ".com" and try again sub rcx, argbuf.size, 5 scasb ax0, zero ; find null-term mov b[ax0+0], '.' ; ".com" mov b[ax0+1], 'c' mov b[ax0+2], 'o' mov b[ax0+3], 'm' nul b[ax0+4] ; try again mov rax, Sys.OpenFile mov ax0, argv0 trap 0 ; still no good? bltz rax, .exec_not_found .do_exec: ; load file into memory mov ax0, rax mov ax1, 0x108000 ; + 32KB mov ax2, 0x8000 mov rax, Sys.ReadFile trap 0 ; save load address mov rcx, rax ; close file mov rax, Sys.CloseFile trap 0 ; read anything? bltz rcx, .couldnt_read jrcxz .empty_file ; all good, let's go mov rax, Sys.ExecuteInFrame mov ax0, 0x108000 ; + 32KB mov ax1, 5 trap 0 ; unreachable jmp abort # 229 "command.k" 2 # 1 "usr/cmd-fsmisc.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. .handle_ERASE: mov rax, Sys.RemoveFile mov ax0, q[argv1pos] bzr ax0, .need_params trap 0 bltz rax, .couldnt_remove jmp .handle_MAKE ; re-create it back .handle_MAKE: mov rax, Sys.CreateFile mov ax0, q[argv1pos] bzr ax0, .need_params trap 0 bltz rax, .couldnt_open jmp .print_prompt .handle_PRINT: mov rax, Sys.OpenFile mov ax0, q[argv1pos] bzr ax0, .need_params trap 0 bltz rax, .file_not_found mov ax0, rax mov ax1, 0x108000 ; + 32KB mov ax2, 0x8000 mov rax, Sys.ReadFile trap 0 mov rcx, rax mov rax, Sys.CloseFile trap 0 bltz rcx, .couldnt_read jrcxz .empty_file call nprint, 0x108000 ; + 32KB, rcx jmp .print_prompt .handle_REMOVE: mov rax, Sys.RemoveFile mov ax0, q[argv1pos] bzr ax0, .need_params trap 0 bltz rax, .couldnt_remove jmp .print_prompt # 230 "command.k" 2 .handle_CLS: prn 0x8BF00001 jmp .print_prompt .handle_CRASH: jmp abort .handle_DATE: call GetTimeUTC mov rcx, b[rax+4] inc rcx push b[rax+3], rcx, w[rax+6] call printf, .datefmt inc rsp, 40 jmp .print_prompt .datefmt = "%d/%d/%d\n" .handle_DUMP: dump jmp .print_prompt .handle_ECHO: mov rax, q[argv1pos] jraxz .echo.end call print, rax .echo.end: prn 10 jmp .print_prompt .handle_EXIT: mov rax, Sys.Shutdown trap 0 jmp .print_prompt .handle_HALT: mov rax, Sys.EnterHaltMode trap 0 jmp .print_prompt .handle_PROMPT: mov ax0, prompt mov ax1, q[argv1pos] bzr ax1, .need_params call strcpy jmp .print_prompt .handle_TIME: call GetTimeUTC push b[rax], b[rax+1], b[rax+2] call printf, .timefmt inc rsp, 24 jmp .print_prompt .timefmt = "%d:%d:%d\n" .handle_VERS: call print, cmd.versionstr prn 10 jmp .print_prompt .handle_HELP: call print, .helpmsg call print, .helpmsg.cls call print, .helpmsg.date call print, .helpmsg.dir call print, .helpmsg.dump call print, .helpmsg.echo call print, .helpmsg.erase call print, .helpmsg.exit call print, .helpmsg.help call print, .helpmsg.halt call print, .helpmsg.make call print, .helpmsg.print call print, .helpmsg.prompt call print, .helpmsg.remove call print, .helpmsg.time call print, .helpmsg.ver jmp .print_prompt .helpmsg = "The following commands are built-in:\n" .helpmsg.cls = " CLS Clear screen\n" .helpmsg.date = " DATE Display current date\n" .helpmsg.dir = " DIR Print contents of current directory\n" .helpmsg.dump = " DUMP Toggles debug instruction dumping\n" .helpmsg.echo = " ECHO Write arguments to standard output\n" .helpmsg.erase = " ERASE Clear a file, making it blank\n" .helpmsg.exit = " EXIT Initiate machine shutdown\n" .helpmsg.help = " HELP Display these messages\n" .helpmsg.halt = " HALT Put processor in halt mode\n" .helpmsg.make = " MAKE Create an empty file\n" .helpmsg.print = " PRINT Display contents of text file\n" .helpmsg.prompt = " PROMPT Change the command line prompt\n" .helpmsg.remove = " REMOVE Delete a file (permanently)\n" .helpmsg.time = " TIME Display current time of day\n" .helpmsg.ver = " VERS Display current COMMAND.COM version\n" .exec_not_found: push argv0 call printf, .enf_errmsg inc rsp, 8 jmp .print_prompt .enf_errmsg = "%s: file not found\n" .file_not_found: push q[argv1pos], argv0 call printf, .fnf_errmsg inc rsp, 16 jmp .print_prompt .fnf_errmsg = "%s: %s: file not found\n" .empty_file: push q[argv1pos], argv0 call printf, .ef_errmsg inc rsp, 16 jmp .print_prompt .ef_errmsg = "%s: %s: file was empty\n" .couldnt_open: push q[argv1pos], argv0 call printf, .cno_errmsg inc rsp, 16 jmp .print_prompt .cno_errmsg = "%s: %s: an error occured while opening file\n" .couldnt_remove: push q[argv1pos], argv0 call printf, .cne_errmsg inc rsp, 16 jmp .print_prompt .cne_errmsg = "%s: %s: an error occured while removing file\n" .couldnt_read: push q[argv1pos], argv0 call printf, .cnr_errmsg inc rsp, 16 jmp .print_prompt .cnr_errmsg = "%s: %s: an error occured while reading file\n" .need_params: call print, argv0 call print, .np_errmsg jmp .print_prompt .np_errmsg = ": need more parameters\n"