diff --git a/LICENSE.MD b/LICENSE.MD index 069c4d7..3bdaf2c 100644 --- a/LICENSE.MD +++ b/LICENSE.MD @@ -1,23 +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. +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 index 2a8b8d7..f2a2b23 100644 --- a/README.MD +++ b/README.MD @@ -1,63 +1,63 @@ -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. The data, stack, and -extra segments are set at the beginning of the RAM and the system stack -at the end of the address space. Programs are loaded at address 0x2000. -The stack is reset back to the end of the address space after a program -has finished running. - -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). +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. The data, stack, and +extra segments are set at the beginning of the RAM and the system stack +at the end of the address space. Programs are loaded at address 0x2000. +The stack is reset back to the end of the address space after a program +has finished running. + +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/src/BOOT.ASM b/src/BOOT.ASM index b0e349f..ffa9f04 100644 --- a/src/BOOT.ASM +++ b/src/BOOT.ASM @@ -1,243 +1,243 @@ -CPU 8086 -ORG 0x7c00 - -jmp start -nop - -;Disk description tables - -%ifdef F1440 -;1.44 MB 3.5" floppy disk (enable with the argument -d F1440 when building) -oemlabel db "ETTINOS " -sectorsize dw 0x200 ;bytes -clustersize db 0x1 ;sectors -bootsectors dw 0x1 -fats db 0x2 -rootentries dw 0xe0 -logicalsectors dw 0xb40 -mediadescriptor db 0xf0 -sectorsperfat dw 0x9 -sectorspertrack dw 0x12 -sides dw 0x2 -hiddensectors dd 0x0 -largesectors dd 0x0 -driveid dw 0x0 -drivesignature db 0x29 -volumeid dd 0x0 -volumelabel db "ETTINOS " -filesystem db "FAT12 " - -%else -;360 KiB 5.25" floppy disk (default) -oemlabel db "ETTINOS " -sectorsize dw 0x200 ;bytes -clustersize db 0x2 ;sectors -bootsectors dw 0x1 -fats db 0x2 -rootentries dw 0x70 -logicalsectors dw 0x2d0 -mediadescriptor db 0xfd -sectorsperfat dw 0x2 -sectorspertrack dw 0x9 -sides dw 0x2 -hiddensectors dd 0x0 -largesectors dd 0x0 -driveid dw 0x0 -drivesignature db 0x29 -volumeid dd 0x0 -volumelabel db "ETTINOS " -filesystem db "FAT12 " - -%endif - -start: - -;Setup -;Set up the data, stack, and extra segments -mov ax, 0x0 -mov ds, ax -mov ss, ax -mov es, ax -;Set up the stack -cli -mov sp, 0x0 -sti -;Store the boot drive number -mov [bootdrive], dl - -;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, buffer -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 system entry -;Set DI to the root -mov di, buffer -;Initialise the search loop -mov cx, word [rootentries] -mov ax, 0x0 -search: -;Store CX in the stack -push cx -;Check for the system entry -mov si, sysfile -mov cx, 0xb -rep cmpsb -je loadentry -;Set DI to the next entry -add ax, 0x20 -mov di, buffer -add di, ax -;Load CX from the stack -pop cx -loop search - -;Print an error message if the system is not found -mov si, errormsg -printerror: -;Load a character -lodsb -;Check for the string end -cmp al, 0x0 -je $ -;Print the character -mov ah, 0xe -int 0x10 -;Repeat -jmp printerror - -;Load the system 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, buffer -mov bx, di -;Set the size -mov ax, [sectorsperfat] -;Load -mov ah, 0x2 -int 0x13 - -;Load the system 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, buffer -add si, ax -mov ax, word [ds:si] -or dx, dx -jz even -odd: -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 boot -mov ax, [sectorsize] -mul word [clustersize] -add word [pointer], ax -jmp loadcluster - -;Clear the stack and boot the system -boot: -;Clear -pop cx -pop bx -;Boot -jmp 0x0:0x500 - -;Data -bootdrive db 0x0 -sysfile db "SYSTEM BIN" -errormsg db "System not found", 0xd, 0xa, 0x0 -cluster dw 0x0 -pointer dw 0x500 - -;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 - -;Pad the binary to a full sector and make the disk bootable -;Padding -times 0x1fe-($-$$) db 0x0 -;Boot signature -dw 0xaa55 - -;File system buffer -buffer: +CPU 8086 +ORG 0x7c00 + +jmp start +nop + +;Disk description tables + +%ifdef F1440 +;1.44 MB 3.5" floppy disk (enable with the argument -d F1440 when building) +oemlabel db "ETTINOS " +sectorsize dw 0x200 ;bytes +clustersize db 0x1 ;sectors +bootsectors dw 0x1 +fats db 0x2 +rootentries dw 0xe0 +logicalsectors dw 0xb40 +mediadescriptor db 0xf0 +sectorsperfat dw 0x9 +sectorspertrack dw 0x12 +sides dw 0x2 +hiddensectors dd 0x0 +largesectors dd 0x0 +driveid dw 0x0 +drivesignature db 0x29 +volumeid dd 0x0 +volumelabel db "ETTINOS " +filesystem db "FAT12 " + +%else +;360 KiB 5.25" floppy disk (default) +oemlabel db "ETTINOS " +sectorsize dw 0x200 ;bytes +clustersize db 0x2 ;sectors +bootsectors dw 0x1 +fats db 0x2 +rootentries dw 0x70 +logicalsectors dw 0x2d0 +mediadescriptor db 0xfd +sectorsperfat dw 0x2 +sectorspertrack dw 0x9 +sides dw 0x2 +hiddensectors dd 0x0 +largesectors dd 0x0 +driveid dw 0x0 +drivesignature db 0x29 +volumeid dd 0x0 +volumelabel db "ETTINOS " +filesystem db "FAT12 " + +%endif + +start: + +;Setup +;Set up the data, stack, and extra segments +mov ax, 0x0 +mov ds, ax +mov ss, ax +mov es, ax +;Set up the stack +cli +mov sp, 0x0 +sti +;Store the boot drive number +mov [bootdrive], dl + +;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, buffer +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 system entry +;Set DI to the root +mov di, buffer +;Initialise the search loop +mov cx, word [rootentries] +mov ax, 0x0 +search: +;Store CX in the stack +push cx +;Check for the system entry +mov si, sysfile +mov cx, 0xb +rep cmpsb +je loadentry +;Set DI to the next entry +add ax, 0x20 +mov di, buffer +add di, ax +;Load CX from the stack +pop cx +loop search + +;Print an error message if the system is not found +mov si, errormsg +printerror: +;Load a character +lodsb +;Check for the string end +cmp al, 0x0 +je $ +;Print the character +mov ah, 0xe +int 0x10 +;Repeat +jmp printerror + +;Load the system 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, buffer +mov bx, di +;Set the size +mov ax, [sectorsperfat] +;Load +mov ah, 0x2 +int 0x13 + +;Load the system 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, buffer +add si, ax +mov ax, word [ds:si] +or dx, dx +jz even +odd: +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 boot +mov ax, [sectorsize] +mul word [clustersize] +add word [pointer], ax +jmp loadcluster + +;Clear the stack and boot the system +boot: +;Clear +pop cx +pop bx +;Boot +jmp 0x0:0x500 + +;Data +bootdrive db 0x0 +sysfile db "SYSTEM BIN" +errormsg db "System not found", 0xd, 0xa, 0x0 +cluster dw 0x0 +pointer dw 0x500 + +;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 + +;Pad the binary to a full sector and make the disk bootable +;Padding +times 0x1fe-($-$$) db 0x0 +;Boot signature +dw 0xaa55 + +;File system buffer +buffer: diff --git a/src/HELLO.ASM b/src/HELLO.ASM index 44f0ab6..d892647 100644 --- a/src/HELLO.ASM +++ b/src/HELLO.ASM @@ -1,11 +1,11 @@ -CPU 8086 -ORG 0x2000 - -;Prints a hello world. -mov si, .hello -mov ah, 0x2 -int 0x21 -int 0x20 - -;Data -.hello db "Hello world!", 0x0 +CPU 8086 +ORG 0x2000 + +;Prints a hello world. +mov si, .hello +mov ah, 0x2 +int 0x21 +int 0x20 + +;Data +.hello db "Hello world!", 0x0 diff --git a/src/LOADF.INC b/src/LOADF.INC index 4486b18..2edde77 100644 --- a/src/LOADF.INC +++ b/src/LOADF.INC @@ -1,304 +1,304 @@ -;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, buffer -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, buffer -;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, buffer -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, buffer -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, buffer -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 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 and return -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 +;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, buffer +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, buffer +;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, buffer +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, buffer +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, buffer +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 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 and return +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/PRINTLN.INC b/src/PRINTLN.INC index 354a9c9..ac5dc55 100644 --- a/src/PRINTLN.INC +++ b/src/PRINTLN.INC @@ -1,12 +1,12 @@ -;Print a string ending in a null from SI followed by a CRLF. - -println: - -;Print the string -mov ah, 0x0 -int 0x21 - -;Print a CRLF -call printcrlf - -iret +;Print a string ending in a null from SI followed by a CRLF. + +println: + +;Print the string +mov ah, 0x0 +int 0x21 + +;Print a CRLF +call printcrlf + +iret diff --git a/src/PRINTSTR.INC b/src/PRINTSTR.INC index 14b0403..57fde6b 100644 --- a/src/PRINTSTR.INC +++ b/src/PRINTSTR.INC @@ -1,28 +1,28 @@ -;Print a string ending in a null from SI. - -printstr: - -;Store the initial registers in the stack -push ax -push si - -;Print the string -.loop: -;Load the current character -lodsb -;Check for the string end -cmp al, 0x0 -je .done -;Print the character -mov ah, 0xe -int 0x10 -;Repeat for the next character -jmp .loop - -.done: - -;Load the initial registers from the stack -pop si -pop ax - -iret +;Print a string ending in a null from SI. + +printstr: + +;Store the initial registers in the stack +push ax +push si + +;Print the string +.loop: +;Load the current character +lodsb +;Check for the string end +cmp al, 0x0 +je .done +;Print the character +mov ah, 0xe +int 0x10 +;Repeat for the next character +jmp .loop + +.done: + +;Load the initial registers from the stack +pop si +pop ax + +iret diff --git a/src/READLN.INC b/src/READLN.INC index 06747b7..45ef949 100644 --- a/src/READLN.INC +++ b/src/READLN.INC @@ -1,12 +1,12 @@ -;Read a string ending in null of at most AL characters to DI until a return and print a CRLF. - -readln: - -;Read the string -mov ah, 0x1 -int 0x21 - -;Print a CRLF -call printcrlf - -iret +;Read a string ending in null of at most AL characters to DI until a return and print a CRLF. + +readln: + +;Read the string +mov ah, 0x1 +int 0x21 + +;Print a CRLF +call printcrlf + +iret diff --git a/src/READSTR.INC b/src/READSTR.INC index d4a57e3..6b1a371 100644 --- a/src/READSTR.INC +++ b/src/READSTR.INC @@ -1,204 +1,204 @@ -;Read a string ending in a null of at most AL characters to DI until a return. - -readstr: - -;Store the initial registers in the stack -push ax -push bx -push cx -push dx -push di - -;Setup -;Store the input length in the stack -mov ah, 0 -push ax -;Initialise the destination with spaces -mov cx, ax -mov al, 0x20 -rep stosb -pop ax -push ax -sub di, ax -;Initialise the cursor pointer in BL and clear BH -mov bx, 0x1 - -.loop: - -;Read a keypress -mov ah, 0x0 -int 0x16 - -;Check for special keys and non-printing characters -;Check for return -cmp al, 0xd -je .return -;Check for backspace -cmp al, 0x8 -je .backspace -;Check for input end -pop dx -push dx -cmp bl, dl -je .loop -;Check for space -cmp al, 0x20 -je .space -;Check for erase -cmp al, 0x9 -je .erase -;Check for non-printing characters -cmp al, 0x1f -jle .loop -cmp al, 0x7f -je .loop - -;Store and print a character -.character: -;Store the character -stosb -;Print the character -mov ah, 0xe -int 0x10 -;Move the cursor pointer -inc bl -jmp .loop - -;Replace the cursor position with a space -.erase: -mov al, 0x20 -jmp .character - -;Move the cursor forward -.space: -call .nextchar -inc di -inc bl -jmp .loop - -;Move the cursor backward -.backspace: -;Check for the input beginning -cmp bl, 0x1 -je .loop -;Move the cursor -call .prevchar -dec di -dec bl -jmp .loop - -;Finish reading the string -.return: - -;Find and remove trailing spaces -;Go to the end of the input -pop ax -mov bh, 0x0 -sub ax, bx -push di -add di, ax -.findtrailing: -;Check for a trailing space -cmp byte [di], 0x20 -je .deltrailing -jmp .end -.deltrailing: -;Delete a trailing space -mov al, 0x0 -stosb -sub di, 0x2 -jmp .findtrailing - -;Move the cursor to the end of the input string -.end: -pop di -.findend: -cmp byte [di], 0x0 -jne .notend -jmp .done -.notend: -call .nextchar -inc di -inc bl -jmp .findend - -.done: - -;Load the initial registers from the stack -pop di -pop dx -pop cx -pop bx -pop ax - -iret - -;Move the cursor forward - -;Move forward within a line -.nextchar: -;Get the cursor position -mov ah, 0x3 -int 0x10 -;Check for the end of the line -cmp dl, 0x4f -je .nextln -;Move -inc dl -mov ah, 0x2 -int 0x10 -ret - -;Move to the beginning of the next line -.nextln: -;Check if the current line is the last on screen -cmp dh, 0x18 -je .scroll -;Move -mov ah, 0x2 -inc dh -mov dl, 0x0 -int 0x10 -ret - -;Scroll the screen up by one line -.scroll: -;Scroll -mov ah, 0x6 -mov al, 0x1 -mov bh, 0x7 -mov ch, 0x0 -mov cl, 0x0 -mov dh, 0x18 -mov dl, 0x4f -int 0x10 -;Move to the beginning of the new line -mov ah, 0x2 -mov bh, 0x0 -mov dl, 0x0 -int 0x10 -ret - -;Move the cursor backward - -;Move backward within a line -.prevchar: -;Get the cursor position -mov ah, 0x3 -int 0x10 -;Check for the beginning of the line -cmp dl, 0x0 -je .prevln -;Move -dec dl -mov ah, 0x2 -int 0x10 -ret - -;Move to the end of the previous line -.prevln: -mov ah, 0x2 -dec dh -mov dl, 0x4f -int 0x10 -ret +;Read a string ending in a null of at most AL characters to DI until a return. + +readstr: + +;Store the initial registers in the stack +push ax +push bx +push cx +push dx +push di + +;Setup +;Store the input length in the stack +mov ah, 0 +push ax +;Initialise the destination with spaces +mov cx, ax +mov al, 0x20 +rep stosb +pop ax +push ax +sub di, ax +;Initialise the cursor pointer in BL and clear BH +mov bx, 0x1 + +.loop: + +;Read a keypress +mov ah, 0x0 +int 0x16 + +;Check for special keys and non-printing characters +;Check for return +cmp al, 0xd +je .return +;Check for backspace +cmp al, 0x8 +je .backspace +;Check for input end +pop dx +push dx +cmp bl, dl +je .loop +;Check for space +cmp al, 0x20 +je .space +;Check for erase +cmp al, 0x9 +je .erase +;Check for non-printing characters +cmp al, 0x1f +jle .loop +cmp al, 0x7f +je .loop + +;Store and print a character +.character: +;Store the character +stosb +;Print the character +mov ah, 0xe +int 0x10 +;Move the cursor pointer +inc bl +jmp .loop + +;Replace the cursor position with a space +.erase: +mov al, 0x20 +jmp .character + +;Move the cursor forward +.space: +call .nextchar +inc di +inc bl +jmp .loop + +;Move the cursor backward +.backspace: +;Check for the input beginning +cmp bl, 0x1 +je .loop +;Move the cursor +call .prevchar +dec di +dec bl +jmp .loop + +;Finish reading the string +.return: + +;Find and remove trailing spaces +;Go to the end of the input +pop ax +mov bh, 0x0 +sub ax, bx +push di +add di, ax +.findtrailing: +;Check for a trailing space +cmp byte [di], 0x20 +je .deltrailing +jmp .end +.deltrailing: +;Delete a trailing space +mov al, 0x0 +stosb +sub di, 0x2 +jmp .findtrailing + +;Move the cursor to the end of the input string +.end: +pop di +.findend: +cmp byte [di], 0x0 +jne .notend +jmp .done +.notend: +call .nextchar +inc di +inc bl +jmp .findend + +.done: + +;Load the initial registers from the stack +pop di +pop dx +pop cx +pop bx +pop ax + +iret + +;Move the cursor forward + +;Move forward within a line +.nextchar: +;Get the cursor position +mov ah, 0x3 +int 0x10 +;Check for the end of the line +cmp dl, 0x4f +je .nextln +;Move +inc dl +mov ah, 0x2 +int 0x10 +ret + +;Move to the beginning of the next line +.nextln: +;Check if the current line is the last on screen +cmp dh, 0x18 +je .scroll +;Move +mov ah, 0x2 +inc dh +mov dl, 0x0 +int 0x10 +ret + +;Scroll the screen up by one line +.scroll: +;Scroll +mov ah, 0x6 +mov al, 0x1 +mov bh, 0x7 +mov ch, 0x0 +mov cl, 0x0 +mov dh, 0x18 +mov dl, 0x4f +int 0x10 +;Move to the beginning of the new line +mov ah, 0x2 +mov bh, 0x0 +mov dl, 0x0 +int 0x10 +ret + +;Move the cursor backward + +;Move backward within a line +.prevchar: +;Get the cursor position +mov ah, 0x3 +int 0x10 +;Check for the beginning of the line +cmp dl, 0x0 +je .prevln +;Move +dec dl +mov ah, 0x2 +int 0x10 +ret + +;Move to the end of the previous line +.prevln: +mov ah, 0x2 +dec dh +mov dl, 0x4f +int 0x10 +ret diff --git a/src/SYSTEM.ASM b/src/SYSTEM.ASM index ac81894..3e09df3 100644 --- a/src/SYSTEM.ASM +++ b/src/SYSTEM.ASM @@ -1,124 +1,124 @@ -CPU 8086 -ORG 0x500 - -jmp start - -;Interrupt handler -;Return to the shell -int0x20: -jmp shell -;Input and output -int0x21: -cmp ah, 0x0 -je printstr -cmp ah, 0x1 -je readstr -cmp ah, 0x2 -je println -cmp ah, 0x3 -je readln -iret -;Disk operations -int0x22: -cmp ah, 0x0 -je loadf -;To do: savef -iret - -;System calls -%include "PRINTSTR.INC" -%include "READSTR.INC" -%include "PRINTLN.INC" -%include "READLN.INC" -%include "LOADF.INC" - -start: - -;Set up the interrupt vectors -;Interrupt 0x20 offset -mov ax, int0x20 -mov [0x80], ax -;Interrupt 0x21 offset -mov ax, int0x21 -mov [0x84], ax -;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 -mov si, welcomemsg -mov ah, 0x2 -int 0x21 - -shell: - -;Re-set the stack -cli -mov sp, 0x0 -sti - -;Prompt for and read a command -;Print a prompt -mov si, prompt -mov ah, 0x0 -int 0x21 -;Read -mov di, input -mov al, 0x4e -mov ah, 0x3 -int 0x21 - -;Load an execute the program -;Check for an empty command -cmp byte [input], 0x0 -jz shell -;Load -mov bx, 0x2000 -mov si, input -mov ah, 0x0 -int 0x22 -;Check for errors -cmp al, 0x1 -je error -;Execute -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 0x4e db 0x0 -crlf db 0xd, 0xa, 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 - -;File system buffer -buffer: +CPU 8086 +ORG 0x500 + +jmp start + +;Interrupt handler +;Return to the shell +int0x20: +jmp shell +;Input and output +int0x21: +cmp ah, 0x0 +je printstr +cmp ah, 0x1 +je readstr +cmp ah, 0x2 +je println +cmp ah, 0x3 +je readln +iret +;Disk operations +int0x22: +cmp ah, 0x0 +je loadf +;To do: savef +iret + +;System calls +%include "PRINTSTR.INC" +%include "READSTR.INC" +%include "PRINTLN.INC" +%include "READLN.INC" +%include "LOADF.INC" + +start: + +;Set up the interrupt vectors +;Interrupt 0x20 offset +mov ax, int0x20 +mov [0x80], ax +;Interrupt 0x21 offset +mov ax, int0x21 +mov [0x84], ax +;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 +mov si, welcomemsg +mov ah, 0x2 +int 0x21 + +shell: + +;Re-set the stack +cli +mov sp, 0x0 +sti + +;Prompt for and read a command +;Print a prompt +mov si, prompt +mov ah, 0x0 +int 0x21 +;Read +mov di, input +mov al, 0x4e +mov ah, 0x3 +int 0x21 + +;Load an execute the program +;Check for an empty command +cmp byte [input], 0x0 +jz shell +;Load +mov bx, 0x2000 +mov si, input +mov ah, 0x0 +int 0x22 +;Check for errors +cmp al, 0x1 +je error +;Execute +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 0x4e db 0x0 +crlf db 0xd, 0xa, 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 + +;File system buffer +buffer: