Write loadf and rewrite the shell to load external programs with it instead of internal calls.

This commit is contained in:
CrazyEttin 2021-06-20 13:48:13 +03:00
parent 59d9c7e89d
commit 2e003c3198
17 changed files with 475 additions and 409 deletions

23
LICENSE.MD Normal file
View File

@ -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.

61
README.MD Normal file
View File

@ -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).

View File

@ -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 ::

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

309
src/LOADF.INC Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
;Prints a string from SI until a null.
;Print a string ending in a null from SI.
printstr:

View File

@ -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

View File

@ -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:

View File

@ -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: