diff --git a/LICENSE.MD b/LICENSE.MD new file mode 100644 index 0000000..069c4d7 --- /dev/null +++ b/LICENSE.MD @@ -0,0 +1,23 @@ +MIT License +=========== + +Copyright (c) 2021 CrazyEttin + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..aea6e75 --- /dev/null +++ b/README.MD @@ -0,0 +1,61 @@ +EttinOS +======= + +EttinOS is a minimalist 16-bit DOS-like hobbyist operating system for +the IBM PC and compatible computers. Its git repository can be found at +https://ahti.space/git/crazyettin/EttinOS. + +System requirements +------------------- + + * An Intel 8086 or compatible CPU + * BIOS, or UEFI in legacy mode + * 64 KiB of RAM + * A floppy disk drive + +Building +-------- + +Build dependencies: + * A Unix-like operating system + * bash + * coreutils + * dosfstools + * mtools + * nasm + +Running make.sh will build EttinOS and create a bootable 360 KiB 5.25" +floppy disk image named EttinOS.img. To get a 1.44 MB 3.5" one instead +use the argument -1440. If you want to use another floppy disk format +you will have to adjust the bootloader disk description tables and +install the system manually. Hard disk drives are not supported. + +Input +----- + +The EttinOS input system is inspired by typewriters. Typing a character +overwrites the cursor location and the erase (=tab) key erases it. The +space and backspace keys move the cursor. + +Programming +----------- + +EttinOS has a flat address space of 64 KiB with the data, stack, and +extra segments set at the beginning of the RAM. Programs are loaded at +address 0x2000. + +System calls: + * Interrupt 0x20: Return to the shell. + * Interrupt 0x21: Input and output: + * AH = 0x0: Print a string ending in a null from SI. + * AH = 0x1: Read a string ending in a null of at most AL + characters to DI until a return. + * AH = 0x2: Print a string ending in a null from SI followed by a + CRLF. + * AH = 0x3: Read a string ending in a null of at most AL + characters to DI until a return and print a CRLF. + * Interrupt 0x22: Disk operations: + * AH = 0x0: Load a file named in SI as a string ending in a null + to the offset BX and set AL to 0x0 if the load was + succesfull and 0x1 if there was an error. + * AH = 0x1: Save a file (under construction). diff --git a/make.sh b/make.sh index cd0971b..1feb043 100755 --- a/make.sh +++ b/make.sh @@ -7,11 +7,12 @@ then fi cd src/ -if [ "$1" == "-F1440" ] +if [ "$1" == "-1440" ] then nasm BOOT.ASM -d F1440 -f bin -o ../bin/BOOT.BIN else nasm BOOT.ASM -f bin -o ../bin/BOOT.BIN fi nasm SYSTEM.ASM -f bin -o ../bin/SYSTEM.BIN +nasm HELLO.ASM -f bin -o ../bin/HELLO.BIN cd .. if [ "$1" == "-1440" ] @@ -20,3 +21,6 @@ if [ "$1" == "-1440" ] fi dd if=bin/BOOT.BIN of=EttinOS.img conv=notrunc bs=512 count=1 mcopy -i EttinOS.img bin/SYSTEM.BIN :: +mcopy -i EttinOS.img bin/HELLO.BIN :: +mcopy -i EttinOS.img README.MD :: +mcopy -i EttinOS.img LICENSE.MD :: diff --git a/src/BOOT.ASM b/src/BOOT.ASM index 4f8f289..9268925 100644 --- a/src/BOOT.ASM +++ b/src/BOOT.ASM @@ -63,8 +63,8 @@ cli mov sp, stack add sp, 0x100 sti -;Store the boot device number -mov [bootdev], dl +;Store the boot drive number +mov [bootdrive], dl ;Load the root ;Set the source @@ -90,7 +90,7 @@ push ax mov ah, 0x2 int 0x13 -;Search the root for the system +;Search the root for the system entry ;Set DI to the root mov di, 0x7f00 ;Initialise the search loop @@ -99,11 +99,11 @@ mov ax, 0x0 search: ;Store CX in the stack push cx -;Check for the system FAT +;Check for the system entry mov si, sysfile mov cx, 0xb rep cmpsb -je loadfat +je loadentry ;Set DI to the next entry add ax, 0x20 mov di, 0x7f00 @@ -127,7 +127,7 @@ int 0x10 jmp printerror ;Load the system entry -loadfat: +loadentry: ;Load CX from the stack pop cx ;Store the first cluster @@ -203,7 +203,7 @@ boot: jmp 0x0:0x500 ;Data -bootdev db 0x0 +bootdrive db 0x0 sysfile db "SYSTEM BIN" errormsg db "System not found", 0xd, 0xa, 0x0 cluster dw 0x0 @@ -227,7 +227,7 @@ mov dh, dl mov ch, al pop bx pop ax -mov dl, byte [bootdev] +mov dl, byte [bootdrive] ret ;Pad the binary to a full sector and make the disk bootable diff --git a/src/CMPSTR.INC b/src/CMPSTR.INC deleted file mode 100644 index 15a8110..0000000 --- a/src/CMPSTR.INC +++ /dev/null @@ -1,41 +0,0 @@ -;Compares strings from SI and DI until a null and either sets the carry flag if they are equal or clears it if not. - -cmpstr: - -;Store the initial registers in the stack -push ax -push bx - -;Compare the strings -.loop: -;Load the current characters -mov al, [si] -mov bl, [di] -;Compare the characters -cmp al, bl -;Check for difference -jne .neq -;Check for the string end -cmp al, 0x0 -je .eq -;Repeat for the next characters -inc si -inc di -jmp .loop - -;Set the carry flag -.eq: -stc -jmp .done - -;Clear the carry flag -.neq: -clc - -.done: - -;Load the initial registers from the stack -pop bx -pop ax - -ret diff --git a/src/ECHO.INC b/src/ECHO.INC deleted file mode 100644 index 9a4900e..0000000 --- a/src/ECHO.INC +++ /dev/null @@ -1,21 +0,0 @@ -;Reads a string to the buffer and prints it. - -echo: - -;Read a string -mov di, buffer -mov al, 0xff -mov ah, 0x3 -int 0x21 - -;Check for an empty string -cmp byte [buffer], 0x0 -je .done - -;Print the string -mov si, buffer -mov ah, 0x2 -int 0x21 - -.done: -ret diff --git a/src/FILEIFY.INC b/src/FILEIFY.INC deleted file mode 100644 index 28a6079..0000000 --- a/src/FILEIFY.INC +++ /dev/null @@ -1,124 +0,0 @@ -;Reads a string, checks if it is a valid 8.3 file name, converts it into FAT formatting, and prints it. - -fileify: - -;Read a string -mov di, buffer -mov al, 0xff -mov ah, 0x3 -int 0x21 - -;Set SI and DI -mov si, buffer -mov di, .file - -;Initialise the name with spaces -mov cx, 0xb -mov al, 0x20 -rep stosb -sub di, 0xb - -;Initialise the length counter for the main part of the name -mov bl, 0x8 - -.nameloop: -;Load a character -lodsb -;Check for a period -cmp al, 0x2e -je .initext -;Check for everything else and convert to upper case -call .checkconv -jmp .nameloop - -.initext: -;Set DI and initialise the length counter for the extension -mov bl, 0x3 -mov di, .file+0x8 - -.extloop: -;Load a character -lodsb -;Check for a period -push ax -cmp al, 0x2e -je .error -pop ax -;Check for everything else and convert to upper case -call .checkconv -jmp .extloop - -.error: -pop ax -mov si, .errormsg -mov ah, 0x2 -int 0x21 -jmp .done - -.print: -pop ax -mov si, .file -mov ah, 0x2 -int 0x21 - -.done: -ret - -.file times 0xc db 0x0 - -.errormsg db "Invalid file name", 0x0 - -.checkconv: - -;Check for the string end -cmp al, 0x0 -je .print - -;Check for the length limit -cmp bl, 0x0 -je .error - -;Check for invalid characters -cmp al, 0x22 -je .error -cmp al, 0x2a -jl .contcheck1 -cmp al, 0x2c -jg .contcheck1 -jmp .error -.contcheck1: -cmp al, 0x2f -je .error -cmp al, 0x3a -jl .contcheck2 -cmp al, 0x3f -jg .contcheck2 -jmp .error -.contcheck2: -cmp al, 0x5b -jl .contcheck3 -cmp al, 0x5d -jg .contcheck3 -jmp .error -.contcheck3: -cmp al, 0x7c -je .error - -;Find and convert lower case letters to upper case -;Check for lower case -cmp al, 0x61 -jl .storech -cmp al, 0x7a -jg .storech -;Convert lower to upper case -sub al, 0x20 - -.storech: - -;Store the character -stosb - -;Increase the counter -dec bl - -ret diff --git a/src/HELLO.INC b/src/HELLO.ASM similarity index 72% rename from src/HELLO.INC rename to src/HELLO.ASM index 7e0c973..44f0ab6 100644 --- a/src/HELLO.INC +++ b/src/HELLO.ASM @@ -1,11 +1,11 @@ +CPU 8086 +ORG 0x2000 + ;Prints a hello world. - -hello: - mov si, .hello mov ah, 0x2 int 0x21 +int 0x20 -ret - +;Data .hello db "Hello world!", 0x0 diff --git a/src/HELP.INC b/src/HELP.INC deleted file mode 100644 index c344478..0000000 --- a/src/HELP.INC +++ /dev/null @@ -1,11 +0,0 @@ -;Prints help. - -help: - -mov si, .help -mov ah, 0x2 -int 0x21 - -ret - -.help db "Input:", 0xd, 0xa, " * Typing a character overwrites the cursor location.", 0xd, 0xa, " * The erase (=tab) key erases the cursor location.", 0xd, 0xa, " * The space and backspace keys move the cursor.", 0xd, 0xa, "Commands:", 0xd, 0xa, " * echo: echoes its input.", 0xd, 0xa, " * fileify: prints its input in FAT format if it is a valid filename.", 0xd, 0xa, " * hello: a hello world program.", 0xd, 0xa, " * help: you are reading it.", 0xd, 0xa, " * keycode: echoes the BIOS code of a key.", 0x0 diff --git a/src/KEYCODE.INC b/src/KEYCODE.INC deleted file mode 100644 index a529793..0000000 --- a/src/KEYCODE.INC +++ /dev/null @@ -1,89 +0,0 @@ -;Reads a keypress and prints its BIOS keycode. - -keycode: - -;Read a keypress -mov ah, 0x0 -int 0x16 - -;Store the keycode -mov [.scan], ah -mov [.ascii], al - -;Print the prefix -mov si, .prefix -mov ah, 0x0 -int 0x21 - -;Convert the scancode to a hex string -mov al, [.scan] -mov di, .keycode -call .byte2hex - -;Convert the ascii value to a hex string -mov al, [.ascii] -mov di, .keycode -add di, 0x2 -call .byte2hex - -;Print the keycode -mov si, .keycode -mov ah, 0x2 -int 0x21 - -ret - -.prefix db "0x", 0x0 - -.scan db 0x0 -.ascii db 0x0 - -.keycode times 0x5 db 0x0 - -.byte2hex: - -;Store the initial registers in the stack -push si -push ax -push bx -push cx - -;Move the byte to AH -mov ah, al - -;Set a key for the hex digits -mov si, .key - -;Set a counter for the two hex digits -mov cx, 0x2 - -.loop: - -;Read a nibble -rol ax, 0x1 -rol ax, 0x1 -rol ax, 0x1 -rol ax, 0x1 -mov bx, ax - -;Convert the nibble to a hex digit -and bx, 0xf -mov bl, [si + bx] - -;Store the hex digit -mov [di], bl - -;Repeat -inc di -dec cx -jnz .loop - -;Load the initial registers from the stack -pop cx -pop bx -pop ax -pop si - -ret - -.key db "0123456789abcdef" diff --git a/src/LOADF.INC b/src/LOADF.INC new file mode 100644 index 0000000..d5bb3a1 --- /dev/null +++ b/src/LOADF.INC @@ -0,0 +1,309 @@ +;Load a file named in SI as a string ending in a null to the offset BX and set AL to 0x0 if the load was succesful and 0x1 if there was an error. + +loadf: + +;Store the initial registers in the stack +push ax +push bx +push cx +push dx +push si +push di + +;Store the offset +mov word [.pointer], bx + +;Set DI at .file and initialise it with spaces +mov di, .file +mov cx, 0xb +mov al, 0x20 +rep stosb +sub di, 0xb + +;Convert .file into FAT formatting + +;Initialise the length counter for the main part of the name +mov bl, 0x8 + +;Convert the main part of the file name +.nameloop: +;Load a character +lodsb +;Check for a period +cmp al, 0x2e +je .initext +;Check for everything else and convert to upper case +call .checkconv +jmp .nameloop + +;Convert the extension +.initext: +;Set DI and initialise the length counter for the extension +mov bl, 0x3 +mov di, .file+0x8 +.extloop: +;Load a character +lodsb +;Check for a period +push ax +cmp al, 0x2e +je .error +pop ax +;Check for everything else and convert to upper case +call .checkconv +jmp .extloop + +;Set the carry flag and print an error message if the file name is invalid +.error: +pop ax +stc +mov si, .errormsg +mov ah, 0x2 +int 0x21 +jmp .done + +;Find and load the file + +.load: +pop ax + +;Load the root +;Set the source +mov ah, 0x0 +mov al, [.fats] +mul word [.sectorsperfat] +add ax, [.bootsectors] +push ax +call .calcsource +;Set the destination +mov si, stack +add si, 0x100 +mov bx, si +;Set the size +push dx +mov ax, [.rootentries] +mov dx, 0x20 +mul dx +mov dx, 0x0 +div word [.sectorsize] +pop dx +push ax +;Load +mov ah, 0x2 +int 0x13 + +;Search the root for the file entry +;Set DI to the root +mov di, stack +add di, 0x100 +;Initialise the search loop +mov cx, word [.rootentries] +mov ax, 0x0 +.search: +;Store CX in the stack +push cx +;Check for the file entry +mov si, .file +mov cx, 0xb +rep cmpsb +je .loadentry +;Set DI to the next entry +add ax, 0x20 +mov di, stack +add di, 0x100 +add di, ax +;Load CX from the stack +pop cx +loop .search + +;Set the carry flag and print an error message if the file is not found +stc +mov si, .errormsg +mov ah, 0x2 +int 0x21 +jmp .clearstack + +;Load the file entry +.loadentry: +;Load CX from the stack +pop cx +;Store the first cluster +mov ax, word [es:di+0xf] +mov word [.cluster], ax +;Set the source +mov ax, 0x1 +call .calcsource +;Set the destination +mov di, stack +add di, 0x100 +mov bx, di +;Set the size +mov ax, [.sectorsperfat] +;Load +mov ah, 0x2 +int 0x13 + +;Load the file + +;Load a cluster +.loadcluster: +;Set the source +pop cx +pop bx +mov ax, word [.cluster] +sub ax, 0x2 +mul byte [.clustersize] +add ax, bx +add ax, cx +push bx +push cx +call .calcsource +;Set the destination +mov bx, word [.pointer] +;Set the size +;mov al, 0x1 +mov al, [.clustersize] +;Load +mov ah, 0x2 +int 0x13 + +;Calculate the next cluster +mov ax, [.cluster] +mov dx, 0x0 +mov bx, 0x3 +mul bx +mov bx, 0x2 +div bx +mov si, stack +add si, 0x100 +add si, ax +mov ax, word [ds:si] +or dx, dx +jz .even +shr ax, 1 +shr ax, 1 +shr ax, 1 +shr ax, 1 +jmp .contcalc +.even: +and ax, 0xfff +.contcalc: +mov word [.cluster], ax +cmp ax, 0xff8 +jge .clearcarry +mov ax, [.sectorsize] +mul word [.clustersize] +add word [.pointer], ax +jmp .loadcluster + +;Clear the carry flag if the load was succesful +.clearcarry: +clc + +;Clear left over values from the stack +.clearstack: +pop cx +pop bx + +.done: + +;Load the initial registers from the stack +pop di +pop si +pop dx +pop cx +pop bx +pop ax + +;Set AL to 0x1 if there was an error and to 0x0 otherwise +jc .setal +mov al, 0x0 +iret +.setal: +mov al, 0x1 +iret + +;Data +.file times 0xb db 0x20 +.errormsg db "File not found", 0x0 +.cluster dw 0x0 +.pointer dw 0x0 + +;These are temporary until i write something to load them from the disk itself +.bootdrive db 0x0 +.sectorsize dw 0x200 ;bytes +.clustersize db 0x2 ;sectors +.bootsectors dw 0x1 +.fats db 0x2 +.rootentries dw 0x70 +.sectorsperfat dw 0x2 +.sectorspertrack dw 0x9 +.sides dw 0x2 + +;Check the file name and convert to upper case +.checkconv: +;Check for the string end +cmp al, 0x0 +je .load +;Check for the length limit +cmp bl, 0x0 +je .error +;Check for invalid characters +cmp al, 0x22 +je .error +cmp al, 0x2a +jl .contcheck1 +cmp al, 0x2c +jg .contcheck1 +jmp .error +.contcheck1: +cmp al, 0x2f +je .error +cmp al, 0x3a +jl .contcheck2 +cmp al, 0x3f +jg .contcheck2 +jmp .error +.contcheck2: +cmp al, 0x5b +jl .contcheck3 +cmp al, 0x5d +jg .contcheck3 +jmp .error +.contcheck3: +cmp al, 0x7c +je .error +;Check for lower case +cmp al, 0x61 +jl .storech +cmp al, 0x7a +jg .storech +;Convert lower to upper case +sub al, 0x20 +.storech: +;Store the character +stosb +;Increase the counter +dec bl +ret + +;Calculate the source arguments for loading data from the disk +.calcsource: +push ax +push bx +mov bx, ax +mov dx, 0x0 +div word [.sectorspertrack] +add dl, 0x1 +mov cl, dl +mov ax, bx +mov dx, 0x0 +div word [.sectorspertrack] +mov dx, 0x0 +div word [.sides] +mov dh, dl +mov ch, al +pop bx +pop ax +mov dl, byte [.bootdrive] +ret diff --git a/src/NEWLINE.INC b/src/NEWLINE.INC deleted file mode 100644 index bc88c22..0000000 --- a/src/NEWLINE.INC +++ /dev/null @@ -1,18 +0,0 @@ -;Prints a newline - -newline: - -;Store the initial registers in the stack -push si - -;Print the newline -mov si, .newline -mov ah, 0x0 -int 0x21 - -;Load the initial registers from the stack -pop si - -ret - -.newline db 0xd, 0xa, 0x0 diff --git a/src/PRINTLN.INC b/src/PRINTLN.INC index 399409b..354a9c9 100644 --- a/src/PRINTLN.INC +++ b/src/PRINTLN.INC @@ -1,4 +1,4 @@ -;Prints a string from SI until a null, followed by a newline. +;Print a string ending in a null from SI followed by a CRLF. println: @@ -6,7 +6,7 @@ println: mov ah, 0x0 int 0x21 -;Print a newline -call newline +;Print a CRLF +call printcrlf iret diff --git a/src/PRINTSTR.INC b/src/PRINTSTR.INC index b21eead..a49fdfb 100644 --- a/src/PRINTSTR.INC +++ b/src/PRINTSTR.INC @@ -1,4 +1,4 @@ -;Prints a string from SI until a null. +;Print a string ending in a null from SI. printstr: diff --git a/src/READLN.INC b/src/READLN.INC index cbb0b7b..06747b7 100644 --- a/src/READLN.INC +++ b/src/READLN.INC @@ -1,4 +1,4 @@ -;Reads a string of at most AL characters to DI until a return and prints a newline. +;Read a string ending in null of at most AL characters to DI until a return and print a CRLF. readln: @@ -6,7 +6,7 @@ readln: mov ah, 0x1 int 0x21 -;Print a newline -call newline +;Print a CRLF +call printcrlf iret diff --git a/src/READSTR.INC b/src/READSTR.INC index d826923..75cb76d 100644 --- a/src/READSTR.INC +++ b/src/READSTR.INC @@ -1,4 +1,4 @@ -;Reads a string of at most AL characters to DI until a return. +;Read a string ending in a null of at most AL characters to DI until a return. readstr: diff --git a/src/SYSTEM.ASM b/src/SYSTEM.ASM index 299103b..f1f8fe4 100644 --- a/src/SYSTEM.ASM +++ b/src/SYSTEM.ASM @@ -4,8 +4,10 @@ ORG 0x500 jmp start ;Interrupt handler +;Return to the shell int0x20: jmp shell +;Input and output int0x21: cmp ah, 0x0 je printstr @@ -16,8 +18,11 @@ je println cmp ah, 0x3 je readln iret +;Disk operations int0x22: -;To do: loading and saving files +cmp ah, 0x0 +je loadf +;To do: savef iret ;System calls @@ -25,17 +30,7 @@ iret %include "READSTR.INC" %include "PRINTLN.INC" %include "READLN.INC" - -;Internal calls -%include "CMPSTR.INC" -%include "NEWLINE.INC" - -;Commands -%include "ECHO.INC" -%include "FILEIFY.INC" -%include "HELLO.INC" -%include "HELP.INC" -%include "KEYCODE.INC" +%include "LOADF.INC" start: @@ -46,20 +41,19 @@ mov sp, stack add sp, 0x100 sti ;Set up the interrupt vectors -;Interrupt 0x20 +;Interrupt 0x20 offset mov ax, int0x20 mov [0x80], ax -mov ax, 0x0 -mov [0x82], ax -;Interrupt 0x21 +;Interrupt 0x21 offset mov ax, int0x21 mov [0x84], ax -mov ax, 0x0 -mov [0x86], ax -;Interrupt 0x22 +;Interrupt 0x22 offset mov ax, int0x22 mov [0x88], ax +;Segments mov ax, 0x0 +mov [0x82], ax +mov [0x86], ax mov [0x8a], ax ;Print a welcome message @@ -69,86 +63,65 @@ int 0x21 shell: +;Re-set up the stack +cli +mov sp, stack +add sp, 0x100 +sti + ;Prompt for and read a command ;Print a prompt mov si, prompt mov ah, 0x0 int 0x21 -;Read a command -mov di, buffer +;Read +mov di, input mov al, 0xff mov ah, 0x3 int 0x21 -;Identify and execute the command -exec: -;Check for no command -cmp byte [buffer], 0x0 -je shell -.echo: -;Check -mov si, buffer -mov di, cmd.echo -call cmpstr -jnc .fileify +;Load an execute the program +;Load +mov bx, 0x2000 +mov si, input +mov ah, 0x0 +int 0x22 +;Check for errors +cmp al, 0x1 +je error ;Execute -call echo -jmp shell -.fileify: -;Check -mov si, buffer -mov di, cmd.fileify -call cmpstr -jnc .hello -;Execute -call fileify -jmp shell -.hello: -;Check -mov si, buffer -mov di, cmd.hello -call cmpstr -jnc .help -;Execute -call hello -jmp shell -.help: -;Check -mov si, buffer -mov di, cmd.help -call cmpstr -jnc .keycode -;Execute -call help -jmp shell -.keycode: -;Check -mov si, buffer -mov di, cmd.keycode -call cmpstr -jnc .error -;Execute -call keycode -jmp shell -.error: +jmp 0x2000 + +;Print an error message and return to the shell +error: +mov bh, 0x0 +mov ah, 0x3 +int 0x10 +dec dh +mov ah, 0x2 +int 0x10 mov si, errormsg mov ah, 0x2 int 0x21 jmp shell +;Data welcomemsg db 0xd, 0xa, "Welcome to EttinOS!", 0xd, 0xa, 0x0 +prompt db "> ", 0x0 +errormsg db "Unknown command", 0x0 +input times 0xff db 0x0 +crlf db 0xd, 0xa, 0x0 -prompt db "> ", 0x0 - -cmd: -.echo db "echo", 0x0 -.fileify db "fileify", 0x0 -.hello db "hello", 0x0 -.help db "help", 0x0 -.keycode db "keycode", 0x0 - -errormsg db "Unknown command", 0x0 - -buffer times 0xff db 0x0 +;Print a CRLF +printcrlf: +;Store the initial registers in the stack +push si +;Print the CRLF +mov si, crlf +mov ah, 0x0 +int 0x21 +;Load the initial registers from the stack +pop si +ret stack: