Compare commits
25 Commits
fbed363099
...
ed
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
245cfa57f3 | ||
|
|
88716c0b64 | ||
|
|
7a9b82d57c | ||
|
|
43d0a020f7 | ||
| beca477ff9 | |||
|
|
02657c3fcd | ||
|
|
a3ed2c801a | ||
| 696f8198ad | |||
| 04ab45dd36 | |||
|
|
4a655e991e | ||
|
|
54d0665456 | ||
|
|
7eea8d0ce1 | ||
|
|
249e02efe3 | ||
|
|
cfe570c531 | ||
|
|
26ee45eb88 | ||
|
|
e4fc63134f | ||
| 0f6f8f33f6 | |||
| e0809f84dc | |||
| 0222a7adb9 | |||
| 7906c48a2e | |||
| 015d4d1fce | |||
| d83871e161 | |||
| 0b9014d846 | |||
|
|
44e4447d21 | ||
|
|
959c90a054 |
BIN
.Makefile.swo
Normal file
BIN
.Makefile.swo
Normal file
Binary file not shown.
3
.gitignore
vendored
Executable file → Normal file
3
.gitignore
vendored
Executable file → Normal file
@@ -1 +1,4 @@
|
||||
*.swp
|
||||
disk_images/*
|
||||
tmp-loop/*
|
||||
detailed.log
|
||||
|
||||
21
Makefile
21
Makefile
@@ -4,26 +4,43 @@ SRC_DIR=source
|
||||
BUILD_DIR=disk_images
|
||||
DATA_DIR=data
|
||||
|
||||
# CD image
|
||||
cdrom: $(BUILD_DIR)/crawos.iso
|
||||
$(BUILD_DIR)/crawos.iso: floppy_image
|
||||
mkdir -p disk_images
|
||||
mkisofs -quiet -V 'CRAWOS' -input-charset iso8859-1 -o disk_images/crawos.iso -b crawos.img disk_images/
|
||||
chmod 755 disk_images/*
|
||||
chgrp users disk_images/*
|
||||
|
||||
# Floppy image
|
||||
# Fat12
|
||||
floppy_image: $(BUILD_DIR)/crawos.img
|
||||
$(BUILD_DIR)/crawos.img: bootloader kernel
|
||||
$(BUILD_DIR)/crawos.img: bootloader kernel check-fat83
|
||||
mkdir -p disk_images
|
||||
dd if=/dev/zero of=$(BUILD_DIR)/crawos.img bs=512 count=2880 # Use dd to make a disk image
|
||||
mkfs.fat -F 12 -n "CRAWOS" $(BUILD_DIR)/crawos.img # Format the disk image with fat12
|
||||
dd if=$(BUILD_DIR)/boot.bin of=$(BUILD_DIR)/crawos.img conv=notrunc # Put boot.bin inside the disk image
|
||||
mcopy -i $(BUILD_DIR)/crawos.img $(BUILD_DIR)/kernel.bin "::kernel.bin" # Put kernel.bin inside the disk image
|
||||
for filename in $(DATA_DIR)/*; do mcopy -i $(BUILD_DIR)/crawos.img $$filename "::/$$(echo $$filename | xargs -n 1 basename)"; done
|
||||
|
||||
|
||||
# Bootloader
|
||||
bootloader: $(BUILD_DIR)/boot.bin
|
||||
$(BUILD_DIR)/boot.bin:
|
||||
mkdir -p disk_images
|
||||
$(ASM) $(SRC_DIR)/bootload/boot.asm -f bin -o $ $(BUILD_DIR)/boot.bin
|
||||
|
||||
# Kernel
|
||||
kernel: $(BUILD_DIR)/kernel.bin
|
||||
$(BUILD_DIR)/kernel.bin:
|
||||
mkdir -p disk_images
|
||||
$(ASM) $(SRC_DIR)/kernel/kernel.asm -f bin -o $ $(BUILD_DIR)/kernel.bin
|
||||
|
||||
check-fat83:
|
||||
@echo "Checking filenames for FAT 8.3 compliance..."
|
||||
@find ./data -type f | awk -F/ '{print $$NF}' | \
|
||||
awk -F. 'length($$1)>8 || length($$2)>3 || NF>2 {print "Invalid FAT 8.3 filename:", $$0; bad=1} END{exit bad}'
|
||||
|
||||
# Clean
|
||||
clean:
|
||||
rm -f disk-images/*
|
||||
rm -f disk_images/*
|
||||
|
||||
68
README.md
Executable file → Normal file
68
README.md
Executable file → Normal file
@@ -1,2 +1,66 @@
|
||||
Kernel written in Assmebly<br/>
|
||||
I'm using a modified JazzOS Bootloader<br/>
|
||||
# What is this?
|
||||
This is a simple implementation of a computer kernel<br/>
|
||||
written in pure x86 assembly with features including
|
||||
- Read/Write disk operations
|
||||
- The ability to boot from the BIOS using the [JazzOS Bootloader](https://raw.githubusercontent.com/scprogramming/JazzOS/refs/heads/main/src/bootloader/boot.asm)
|
||||
- A modified [MikeOS BASIC interpreter](https://mikeos.sourceforge.net/handbook-appdev-basic.html)
|
||||
- A functional POSIX inspired CLI
|
||||
|
||||
# What is it for?
|
||||
This project is aimed at people wanting to learn<br/>
|
||||
more about kernels and low level system functionality.<br/>
|
||||
In order to help with comprehension, there are a lot<br/>
|
||||
of inline comments descibing what each part does.
|
||||
|
||||
# Commands
|
||||
- CAT <filename>: outputs the contents of the file.
|
||||
- LS: outputs a list of files on the system .
|
||||
- CLEAR: clears the screen.
|
||||
- REBOOT: reboots the system, this can also be done with [esc].
|
||||
- BAS <filename>: runs a basic script.
|
||||
|
||||
# Running it
|
||||
In order to build this project on a Linux system run<br/>
|
||||
the following commands.
|
||||
```
|
||||
git clone https://git.javalsai.tuxcord.net/deadvey/crawos.git
|
||||
cd crawos
|
||||
sudo make
|
||||
```
|
||||
This will create a directory called 'disk_images' which<br/>
|
||||
contains optical and floppy images that can be ran in a<br/>
|
||||
virtual machine such as QEMU with
|
||||
```
|
||||
qemu-system-i386 -drive file=disk_images/crawos.img,if=floppy,format=raw
|
||||
```
|
||||
Or it can be burnt to a physical disk to be booted from<br/>
|
||||
hardware.
|
||||
> [!NOTE]
|
||||
> The VM setup has only been tested on OpenSUSE and NixOS Linux,<br/>
|
||||
> please report any bugs.
|
||||
|
||||
# Development
|
||||
In order to run this project in testing mode you can use<br/>
|
||||
The 'test-linux.sh' bash script which will assemble and<br/>
|
||||
boot the virtual machine (QEMU) for testing purposes.<br/>
|
||||
```
|
||||
./test-linux.sh
|
||||
```
|
||||
This will dump the contents of memory in /dev/shm/qemu-ram<br/>
|
||||
To allow for easier debugging.
|
||||
|
||||
# BASIC development
|
||||
Please refer to the [MikeOS BASIC documentation](https://mikeos.sourceforge.net/handbook-appdev-basic.html)<br/>
|
||||
docs on writing BASIC code. In order to create new<br/>
|
||||
files, place them in the /data/ directory, note the file<br/>
|
||||
names are limited to 11 characters due to FAT12 limitations<br/>
|
||||
(8 for the name and 3 for the extension).
|
||||
|
||||
# Legal information
|
||||
As a consequence of recent legislative activity in [California](https://leginfo.legislature.ca.gov/faces/billTextClient.xhtml?bill_id=202520260AB1043) and [Colororado](https://leg.colorado.gov/bill_files/111670/download):
|
||||
|
||||
* California residents may no longer use CrawOS after Jan 1st, 2027.
|
||||
* Colorado residents may no longer use CrawOS after Jan 1st, 2028.
|
||||
|
||||
CrawOS is probably an operating system under these laws. However, it
|
||||
does not, cannot and will not implement age verification.
|
||||
|
||||
0
build-linux.sh
Executable file → Normal file
0
build-linux.sh
Executable file → Normal file
14
data/README.md
Normal file
14
data/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# What is this?
|
||||
This is a simple implementation of a computer kernel
|
||||
written in pure x86 assembly with features including
|
||||
- Read/Write disk operations
|
||||
- The ability to boot from the BIOS using the JazzOS Bootloader
|
||||
- A modified MikeOS BASIC interpreter
|
||||
- A functional POSIX inspired CLI
|
||||
|
||||
# Commands
|
||||
- CAT <filename>: outputs the contents of the file.
|
||||
- LS: outputs a list of files on the system .
|
||||
- CLEAR: clears the screen.
|
||||
- REBOOT: or esc reboots the system
|
||||
- BAS <filename>: runs a basic script.
|
||||
11
data/fb.bas
Normal file
11
data/fb.bas
Normal file
@@ -0,0 +1,11 @@
|
||||
# A cool fizzbuzz program
|
||||
PRINT "FizzBuzz!"
|
||||
FOR A = 1 TO 16
|
||||
PRINT A ; # Prints the number
|
||||
B = A % 3
|
||||
C = A % 5
|
||||
IF B = 0 then PRINT "Fizz" ;
|
||||
IF C = 0 then PRINT "Buzz" ;
|
||||
PRINT ""
|
||||
NEXT A
|
||||
END
|
||||
@@ -1 +0,0 @@
|
||||
output 'Hello World'
|
||||
5
data/hello.bas
Normal file
5
data/hello.bas
Normal file
@@ -0,0 +1,5 @@
|
||||
PRINT "Hello, what is your name?"
|
||||
INPUT $1
|
||||
PRINT "Hello " ;
|
||||
PRINT $1
|
||||
END
|
||||
2
data/test.asm
Normal file
2
data/test.asm
Normal file
@@ -0,0 +1,2 @@
|
||||
X = 100
|
||||
PRINT X
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
449
docs/BASIC_DEVELOPMENT.md
Normal file
449
docs/BASIC_DEVELOPMENT.md
Normal file
@@ -0,0 +1,449 @@
|
||||
This kernel uses a modified version of the MikeOS BASIC interpreter.<br/>
|
||||
# Overview
|
||||
## Features
|
||||
The MikeOS BASIC interpreter runs a simple dialect of the BASIC programming language. There are commands for taking input, handling the screen, performing nested loops, loading/saving files, and so forth. You will find a full list of the included instructions later in this document. Here are the essentials you need to know.
|
||||
|
||||
Numeric variables -- These are A to Z, each storing one positive integer word (ie 0 to 65535). The R and S variables have special roles with LOAD and SAVE commands, as explained in the instruction list below.
|
||||
String variables -- These are $1 to $8, each 128 bytes long.
|
||||
Arrays -- You can use string variables as arrays via the PEEK and POKE commands. For instance, X = & $1 places the memory location of the $1 string variable into X. You can then put data into it with eg POKE 77 X (put 77 into the memory location pointed to by X).
|
||||
Labels -- Such as box_loop: etc. Used by GOTO and GOSUB, they must have a trailing colon and be at the start of a line.
|
||||
Ending -- Programs must finish with an END statement.
|
||||
|
||||
GOSUB calls can be nested up to ten times. FOR loops can be nested using different variables (eg you can have a FOR X = 1 TO 10 ... NEXT X loop surrounding a FOR Y = 30 TO 50 loop). You can enter code in uppercase or lowercase -- the interpreter doesn't mind either way. However, labels are case-sensitive.
|
||||
|
||||
If a MikeOS BASIC program is run from the command line, and one or more parameters was provided with the command, they are copied into the $1 string when the program starts.
|
||||
|
||||
## Example
|
||||
|
||||
Here's a small example program demonstrating a FizzBuzz program.
|
||||
|
||||
```
|
||||
# A cool fizzbuzz program
|
||||
PRINT "FizzBuzz!"
|
||||
FOR A = 1 TO 16
|
||||
PRINT A ; # Prints the number
|
||||
B = A % 3
|
||||
C = A % 5
|
||||
IF B = 0 then PRINT "Fizz" ;
|
||||
IF C = 0 then PRINT "Buzz" ;
|
||||
PRINT ""
|
||||
NEXT A
|
||||
END
|
||||
```
|
||||
|
||||
This example should be mostly self-explanatory. You can see that the subroutine is indented with tabs, but that's not necessary if you don't want it. You can follow IF ... THEN with any other instruction. Regarding this part:
|
||||
```
|
||||
PRINT A ; # Prints the number
|
||||
B = A % 3
|
||||
```
|
||||
|
||||
The space and semi-colon character (;) after the quoted string tells the interpreter not to print a newline after the string. So here, we print the user's name on the same line. You can do this with numerical variables as well, eg PRINT X ; etc.
|
||||
|
||||
See the Samples section at the end for more demonstration programs.
|
||||
|
||||
## Assignment
|
||||
|
||||
The following are valid ways to assign numeric variables in MikeOS BASIC:
|
||||
|
||||
```
|
||||
a = 10
|
||||
a = b
|
||||
a = b + 10
|
||||
a = b + c
|
||||
a = b - 10
|
||||
a = b - c
|
||||
a = b * 10
|
||||
a = b * c
|
||||
a = b / 10
|
||||
a = b / c
|
||||
a = b % 10
|
||||
a = b % c
|
||||
```
|
||||
|
||||
So you can use combinations of numbers and variables. Note that you can perform multiple operations in the same line:
|
||||
```
|
||||
a = b + c * 2 / 3
|
||||
```
|
||||
But note that there is no operator precedence; the calculation is simply worked out one step at a time from left to right. For string variables:
|
||||
```
|
||||
$1 = "Hello"
|
||||
$2 = $1
|
||||
```
|
||||
You can get the location of a string variable to use as an array:
|
||||
```
|
||||
x = & $3
|
||||
```
|
||||
You can get variables from the user with INPUT like this:
|
||||
```
|
||||
input x
|
||||
input $1
|
||||
```
|
||||
|
||||
## Keywords
|
||||
|
||||
## The editor
|
||||
|
||||
You can run your .BAS programs by using the BAS core util eg.
|
||||
```
|
||||
bas <filename>.bas
|
||||
```
|
||||
|
||||
# Instructions
|
||||
## BREAK
|
||||
|
||||
Halts execution of the BASIC program and prints the line number in the BASIC file. Useful for debugging.
|
||||
|
||||
## CALL
|
||||
|
||||
Moves machine code execution to the specified point in RAM (using the x86 call instruction). The code must be terminated with a ret (C3 hex, 195 decimal) instruction. In this example, we simply add a ret instruction into RAM location 40000 and call it, which returns control straight back to the BASIC interpreter:
|
||||
```
|
||||
poke 195 40000
|
||||
call 40000
|
||||
```
|
||||
|
||||
## CASE
|
||||
|
||||
Changes the contents of a string to all upper-case or lower-case.
|
||||
```
|
||||
case lower $1
|
||||
case upper $2
|
||||
```
|
||||
|
||||
## CLS
|
||||
|
||||
Clears the screen and returns the cursor to the top-left corner of the screen. Example:
|
||||
```
|
||||
cls
|
||||
```
|
||||
|
||||
## CURSOR
|
||||
|
||||
Determines whether to show the text cursor or not. Example:
|
||||
|
||||
cursor off
|
||||
print "The cursor is off for five seconds!"
|
||||
pause 50
|
||||
cursor on
|
||||
print "And now it's back on."
|
||||
|
||||
|
||||
## CURSCHAR
|
||||
|
||||
Stores the character underneath the cursor location into the specified variable. Example:
|
||||
```
|
||||
move 0 0
|
||||
print "Hello world"
|
||||
|
||||
move 0 0
|
||||
curschar x
|
||||
|
||||
# The next command will print 'H'
|
||||
print chr x
|
||||
|
||||
move 1 0
|
||||
curschar x
|
||||
|
||||
# The next command will print 'e'
|
||||
print chr x
|
||||
```
|
||||
|
||||
## CURSCOL
|
||||
|
||||
Get the colour of the character under the cursor. Example:
|
||||
```
|
||||
move 20 15
|
||||
curscol x
|
||||
```
|
||||
|
||||
## CURSPOS
|
||||
|
||||
Get the position of the cursor. Example:
|
||||
```
|
||||
# First is column, then row
|
||||
curspos a b
|
||||
```
|
||||
|
||||
## DO
|
||||
|
||||
Perform a loop until a condition is met (UNTIL or WHILE). You can also set up an infinite loop with LOOP ENDLESS at the end. Example:
|
||||
```
|
||||
do
|
||||
# Code goes here
|
||||
loop until x = 10
|
||||
```
|
||||
|
||||
## ELSE
|
||||
|
||||
Executes code if the previous IF condition didn't match. Example:
|
||||
```
|
||||
x = 1
|
||||
if x = 1 then print "Hello"
|
||||
else print "Goodbye"
|
||||
```
|
||||
|
||||
## END
|
||||
|
||||
Terminates execution of the BASIC program and hands control back to the operating system.
|
||||
|
||||
## FOR
|
||||
|
||||
Begins a loop, counting upwards using a variable. The loop must be finished with a NEXT instruction and the relevant variable. Example:
|
||||
```
|
||||
for x = 1 to 10
|
||||
print "In a loop! X is " ;
|
||||
print x
|
||||
next x
|
||||
```
|
||||
|
||||
## GETKEY
|
||||
|
||||
Checks the keyboard buffer for a key, and if one has been pressed, places it into the specified variable.
|
||||
```
|
||||
loop:
|
||||
print "Infinite loop until m or Esc is pressed..." ;
|
||||
getkey x
|
||||
if x = 'm' then goto done
|
||||
goto loop
|
||||
|
||||
done:
|
||||
print "Finished loop!"
|
||||
```
|
||||
|
||||
## GOSUB
|
||||
|
||||
Takes a label. It executes a subroutine, which must be finished with a RETURN instruction. You can nest GOSUB routines up to 10 times. Example:
|
||||
```
|
||||
print "About to go into a subroutine..."
|
||||
gosub mylabel
|
||||
print "Subroutine done!"
|
||||
end
|
||||
|
||||
mylabel:
|
||||
print "Inside a GOSUB here!"
|
||||
return
|
||||
```
|
||||
|
||||
## GOTO
|
||||
|
||||
Takes a label, and jumps to that label in the code. Example:
|
||||
```
|
||||
print "Going to miss the next 'PRINT' line of code..."
|
||||
goto skippy
|
||||
|
||||
print "This'll never be printed."
|
||||
|
||||
skippy:
|
||||
print "And now we're back home"
|
||||
```
|
||||
|
||||
## IF
|
||||
|
||||
Executes a command depending on a condition (or multiple conditions with AND). After stating the condition (eg whether one number is bigger than another, or whether two strings match) you must use THEN and follow with another instruction. Examples:
|
||||
```
|
||||
if x = 10 then print "X is 10! Woohoo"
|
||||
|
||||
if x = y then print "X is the same as Y"
|
||||
|
||||
if x = 'm' then print "X contains the letter m"
|
||||
|
||||
if x < y then print "Now X is less than Y"
|
||||
|
||||
if x > y then goto xbiggerthany
|
||||
|
||||
if $1 = "quit" then end
|
||||
|
||||
if $1 = $2 then gosub stringsmatch
|
||||
```
|
||||
|
||||
## INPUT
|
||||
|
||||
Gets input from the user and stores the result into a numeric or string variable. Examples:
|
||||
```
|
||||
input x
|
||||
input $1
|
||||
```
|
||||
|
||||
## LEN
|
||||
|
||||
Stores the length of a string variable in a numeric variable. Example:
|
||||
```
|
||||
$1 = "Hello world"
|
||||
len $1 x
|
||||
```
|
||||
|
||||
## LOAD
|
||||
|
||||
Loads the specified file into RAM at the specified point. The first argument is the filename, and the second the location into which it should be loaded. If the file cannot be found or loaded, the R variable contains 1 after the instruction; otherwise it contains 0 and the S variable contains the file size. Examples:
|
||||
```
|
||||
load "example.txt" 40000
|
||||
if r = 1 then goto fail
|
||||
print "File size is:"
|
||||
print s
|
||||
end
|
||||
|
||||
fail:
|
||||
print "File couldn't be loaded"
|
||||
end
|
||||
|
||||
$1 = "example.txt"
|
||||
x = 40000
|
||||
|
||||
load $1 x
|
||||
if r = 1 then goto fail
|
||||
print "File size is:"
|
||||
print s
|
||||
end
|
||||
|
||||
fail:
|
||||
print "File couldn't be loaded"
|
||||
end
|
||||
```
|
||||
|
||||
## NEXT
|
||||
|
||||
Continues the FOR loop specified previously, and must be followed by a variable. See FOR above. Example:
|
||||
```
|
||||
next x
|
||||
```
|
||||
|
||||
## NUMBER
|
||||
|
||||
Converts strings to numbers and vice versa. Examples:
|
||||
```
|
||||
number $1 a
|
||||
|
||||
number a $1
|
||||
```
|
||||
|
||||
## PAUSE
|
||||
|
||||
Delays execution of the program by the specified 10ths of a second. This ultimately results in a BIOS call and may be slower or faster in some PC emulators. Try it on real hardware to be sure. Example:
|
||||
```
|
||||
print "Now let's wait for three seconds..."
|
||||
pause 30
|
||||
|
||||
print "Hey, and one more, this time with a variable..."
|
||||
x = 10
|
||||
pause x
|
||||
```
|
||||
|
||||
## PAGE
|
||||
|
||||
Switch between working and active (display) pages. Example:
|
||||
```
|
||||
page 1 0
|
||||
```
|
||||
|
||||
## PEEK
|
||||
|
||||
Retrieve the byte stored in the specifed location in RAM. Examples:
|
||||
```
|
||||
peek a 40000
|
||||
print "The number stored in memory location 40000 is..."
|
||||
print a
|
||||
|
||||
x = 32768
|
||||
peek a x
|
||||
```
|
||||
> [!NOTE]
|
||||
> You can use PEEKINT to work with words instead of bytes (up to 65536).
|
||||
|
||||
## POKE
|
||||
|
||||
Insert the byte (0 to 255) value into the specified location in RAM. Example:
|
||||
```
|
||||
print "Putting the number 126 into location 40000 in memory..."
|
||||
poke 126 40000
|
||||
|
||||
print "Now doing the same, but using variables..."
|
||||
x = 126
|
||||
y = 40000
|
||||
poke x y
|
||||
```
|
||||
> [!NOTE]
|
||||
> You can use POKEINT to work with words instead of bytes (up to 65536).
|
||||
|
||||
## PORT
|
||||
|
||||
Sends and receives bytes from the specified port. Examples:
|
||||
```
|
||||
x = 1
|
||||
port out 1234 x
|
||||
port out 1234 15
|
||||
port in 1234 x
|
||||
```
|
||||
|
||||
## PRINT
|
||||
|
||||
Displays text or the contents of a variable onto the screen. This will also move the cursor onto a new line after printing, unless the command is followed by a space and semi-colon. Example:
|
||||
```
|
||||
print "Hello, world!"
|
||||
|
||||
$1 = "Some text"
|
||||
print $1
|
||||
|
||||
x = 123
|
||||
print x
|
||||
|
||||
$2 = "Mike"
|
||||
print "No newlines here, " ;
|
||||
print $2
|
||||
```
|
||||
> [!NOTE]
|
||||
> For numerical variables, the PRINT command also supports two extra keywords:
|
||||
```
|
||||
x = 109
|
||||
print x
|
||||
print chr x
|
||||
print hex x
|
||||
```
|
||||
In the first print command, the output is 109. In the second, the output is the ASCII character for 109 -- that is, 'm'. And in the third command, it shows the hexadecimal equivalent of 109.
|
||||
|
||||
## READ
|
||||
|
||||
Read data bytes from a label, first specifying the offset and a variable into which to read. For instance, in the following example we read a small program and poke it into memory locations 50000 to 50012. We then call that location to run the program:
|
||||
```
|
||||
y = 1
|
||||
|
||||
for x = 50000 to 50012
|
||||
read mydata y a
|
||||
poke a x
|
||||
y = y + 1
|
||||
next x
|
||||
|
||||
call 50000
|
||||
|
||||
waitkey x
|
||||
end
|
||||
|
||||
mydata:
|
||||
190 87 195 232 173 60 195 89 111 33 13 10 0
|
||||
```
|
||||
|
||||
## RETURN
|
||||
|
||||
Switches execution back to the position of the prior GOSUB statement. See GOSUB above for more information. Example:
|
||||
```
|
||||
return
|
||||
```
|
||||
|
||||
|
||||
## STRING
|
||||
|
||||
Get or set bytes in a string variable, specified by an offset. Examples:
|
||||
```
|
||||
$1 = "Hello world"
|
||||
|
||||
rem *** 121 = ASCII for "y" character ***
|
||||
b = 121
|
||||
|
||||
string set $1 5 b
|
||||
|
||||
rem *** Now $1 contains "Helly world" ***
|
||||
print $1
|
||||
```
|
||||
|
||||
# Modifictions
|
||||
The modifications include
|
||||
- Inline comments starting with a hashtag (#)
|
||||
- Start of line whitespaces for indentation, spaces or tabs (not fussy about indentation depth)
|
||||
4
docs/MEMORY_LAYOUT.md
Normal file
4
docs/MEMORY_LAYOUT.md
Normal file
@@ -0,0 +1,4 @@
|
||||
The root directory is from byte 24000h to 26FFFh<br/>
|
||||
The currently opened file's metadata is from 27000h to 2701Fh<br/>
|
||||
The to be outputted string is stored from 27020h to 27FFFh<br/>
|
||||
The currently opened file is from 28000h<br/>
|
||||
@@ -5,27 +5,27 @@ jmp short main
|
||||
nop
|
||||
|
||||
; Define Fat12 header
|
||||
bdb_oem: db 'MSWIN4.1'
|
||||
bdb_bytes_per_sector: dw 512
|
||||
bdb_sectors_per_cluster: db 1
|
||||
bdb_reserved_sectors: dw 1
|
||||
bdb_fat_count: db 2
|
||||
bdb_dir_entries_count: dw 0E0h
|
||||
bdb_total_sectors: dw 2880
|
||||
bdb_media_descriptor_type: db 0F0h
|
||||
bdb_sectors_per_fat: dw 9
|
||||
bdb_sectors_per_track: dw 18
|
||||
bdb_heads: dw 2
|
||||
bdb_hidden_sectors: dd 0
|
||||
bdb_large_sector_count: dd 0
|
||||
bdb_oem: db 'MSWIN4.1' ; ignore
|
||||
bdb_bytes_per_sector: dw 200h ; = 512d
|
||||
bdb_sectors_per_cluster: db 01h ; sector = cluster
|
||||
bdb_reserved_sectors: dw 01h
|
||||
bdb_fat_count: db 02h ; We've got a fat1 and fat2
|
||||
bdb_dir_entries_count: dw 0E0h ; = 224d Maximum number of root directory entries
|
||||
bdb_total_sectors: dw 0B40h ; = 2880d
|
||||
bdb_media_descriptor_type: db 0F0h ; ignore
|
||||
bdb_sectors_per_fat: dw 09h
|
||||
bdb_sectors_per_track: dw 12h ; = 18d
|
||||
bdb_number_of_heads: dw 02h ; top and bottom of the floppy disk
|
||||
bdb_hidden_sectors: dd 0 ; ignore
|
||||
bdb_large_sector_count: dd 0 ; total sector count for fat32 (0 for fat12 or fat16)
|
||||
|
||||
; extended boot record
|
||||
ebr_drive_number: db 0
|
||||
db 0
|
||||
ebr_signature: db 29h
|
||||
ebr_volume_id: db 12h, 34h, 56h, 78h
|
||||
ebr_volume_label: db 'CrawShaw OS'
|
||||
ebr_system_id: db 'FAT12 '
|
||||
ebr_drive_number: db 0 ; ignore
|
||||
db 0 ; ignore
|
||||
ebr_signature: db 29h ; boot signature, indicates that the next three fields are present (0x29)
|
||||
ebr_volume_id: db 12h, 34h, 56h, 78h ; unique id for volume tracking
|
||||
ebr_volume_label: db 'CrawShaw OS' ; must be 11 bytes
|
||||
ebr_system_id: db 'FAT12 ' ; must be 8 bytes
|
||||
|
||||
main:
|
||||
; Setup registers
|
||||
@@ -54,46 +54,54 @@ main:
|
||||
; root dir: gives location of data
|
||||
; data: stores the actual data
|
||||
|
||||
; Get LBA of root dir
|
||||
mov ax, [bdb_sectors_per_fat]
|
||||
mov bl, [bdb_fat_count]
|
||||
; Get LBA of root dir (sectors per fat * fat count) + number of reserved sectors
|
||||
mov ax, [bdb_sectors_per_fat] ; ax = 09h
|
||||
mov bl, [bdb_fat_count] ; bl = 02h
|
||||
xor bh,bh ; clear bh
|
||||
mul bx ; 9 * 2 = 18
|
||||
add ax, [bdb_reserved_sectors] ; The LBA of root dir
|
||||
mul bx ; ax*bx = sectors per fat * fat count = 09h * 02h = 18
|
||||
add ax, [bdb_reserved_sectors] ; then add on the reserved sector (1) = The LBA of root dir = 19d = 13h
|
||||
push ax ; Push to stack
|
||||
; the top of the ax stack now stores the LBA of the root directory
|
||||
|
||||
mov ax, [bdb_dir_entries_count]
|
||||
; determine the size of the root directory
|
||||
mov ax, [bdb_dir_entries_count] ; move the number of root directory entries into ax (E0h)
|
||||
shl ax,5 ; ax *= 32 (shifting 5 times)
|
||||
xor dx,dx ; Clear dx
|
||||
div word [bdb_bytes_per_sector] ;32*num of entries)/bytes per sector
|
||||
test dx,dx ; See if there's a remainder
|
||||
xor dx,dx ; Clear dx (remainder)
|
||||
div word [bdb_bytes_per_sector] ;(32*num of entries)/bytes per sector = total number of sectors we need to read
|
||||
test dx,dx ; See if there's a remainder, ie the root directory doesn't take up a whole number of sectors
|
||||
jz root_dir_after
|
||||
inc ax ; Add one if there's a remainder
|
||||
inc ax ; Add one if there's a remainder (this is like rounding up)
|
||||
|
||||
; read the root directory tree into memory
|
||||
root_dir_after:
|
||||
; read the data from the root directory from disk
|
||||
mov cl, al
|
||||
pop ax ; LBA of root dir
|
||||
mov dl, [ebr_drive_number]
|
||||
mov bx, buffer
|
||||
call disk_read
|
||||
call disk_read ; convert the LBA of the root directory to a CHS
|
||||
|
||||
xor bx,bx ; clear bx
|
||||
mov di, buffer ; Loaded root dir into memory
|
||||
mov di, buffer ; Loaded root dir into memory, now we need to find the kernel
|
||||
; di points to the start of this memory
|
||||
|
||||
; Search for 'KERNEL BIN' in a loop until all root files entries have been checked
|
||||
search_for_kernel:
|
||||
mov si, file_kernel_bin ; Move the name of the kernel into si
|
||||
mov cx, 11
|
||||
mov si, file_kernel_bin ; Move the name of the kernel into si (string pointer)
|
||||
mov cx, 11 ; length of 'KERNEL BIN'
|
||||
push di ; Preserve di
|
||||
repe cmpsb ; Repeat a comparison of bytes between kernel name and current bytes until it finds a match
|
||||
pop di ; Retrieve di
|
||||
je found_kernel
|
||||
|
||||
add di, 32 ; Next directory entry
|
||||
add di, 32 ; increment di to the next directory entry
|
||||
inc bx
|
||||
cmp bx, [bdb_dir_entries_count] ; Have we reached the number of directories that exist
|
||||
jl search_for_kernel ; Repeat search
|
||||
|
||||
jmp kernel_not_found ; If the last dir has been searched, then there is no kernel
|
||||
|
||||
; The kernel has not been found so output an error and halt
|
||||
kernel_not_found:
|
||||
mov si, kernel_load_error
|
||||
call print_string
|
||||
@@ -101,13 +109,15 @@ kernel_not_found:
|
||||
hlt
|
||||
jmp halt
|
||||
|
||||
; The kernel has been found
|
||||
found_kernel:
|
||||
mov si, kernel_found_text
|
||||
call print_string
|
||||
; Find kernel cluster
|
||||
mov ax, [di+26]
|
||||
mov ax, [di+26] ; di is the address of the kernel, 26 is the offset
|
||||
mov [kernel_cluster], ax
|
||||
|
||||
; Setup registers for disk read
|
||||
mov ax, [bdb_reserved_sectors]
|
||||
mov bx, buffer
|
||||
mov cl, [bdb_sectors_per_fat]
|
||||
@@ -160,6 +170,8 @@ next_cluster_after:
|
||||
jmp load_kernel_loop
|
||||
|
||||
read_finish: ; Load kernel
|
||||
mov si, kernel_loading
|
||||
call print_string
|
||||
mov dl, [ebr_drive_number]
|
||||
mov ax, kernel_load_segment
|
||||
mov ds, ax
|
||||
@@ -174,10 +186,35 @@ halt:
|
||||
|
||||
; LBA = index of data segment on disk
|
||||
; CHS = cylinder, header, sector
|
||||
; T = LBA/sectors per track
|
||||
; S = (LBA%sectors per track) + 1
|
||||
; H = T % heads
|
||||
; C = T / headers
|
||||
; input, LBA index: ax
|
||||
; sector number: cx [0-5]
|
||||
; cylinder: cx [6-15]
|
||||
; sector number: cl
|
||||
; cylinder: ch
|
||||
; head: dh
|
||||
; Example where LBA = 50h (CHS = 2,0,9)
|
||||
; ax = 0050h, push this to the stack
|
||||
; dx = 0000h
|
||||
; dx = 50h % 12h = 0008h
|
||||
; ax = 50h / 12h = 0004h
|
||||
; dx = 0009h
|
||||
; cx = 0009h
|
||||
; dx = 0000h
|
||||
; dx = 04h % 02h = 0000h
|
||||
; ax = 04h / 02h = 0002h
|
||||
; dh = 00h (dx = 0000h)
|
||||
; ch = 02h (cx = 0209h)
|
||||
; ah = 00h (ax = 0002h)
|
||||
; cl = 09h OR 00h = 09h (cx = 0209h)
|
||||
; ax = 0050h
|
||||
; dl = 50h (dx = 0050h)
|
||||
; ax = 0050h
|
||||
; thus:
|
||||
; cylinder (ch) = 02h
|
||||
; head (cl) = 00h
|
||||
; sector (dh) = 09h
|
||||
lba_to_chs:
|
||||
push ax
|
||||
push dx
|
||||
@@ -188,19 +225,26 @@ lba_to_chs:
|
||||
mov cx,dx
|
||||
|
||||
xor dx,dx ; clear dx
|
||||
div word [bdb_heads]
|
||||
div word [bdb_number_of_heads]
|
||||
mov dh,dl ; head, dx stores remainder so we move that up 8 bits to dh
|
||||
|
||||
mov dh,dl ; head
|
||||
mov ch,al
|
||||
shl ah, 6
|
||||
or CL, AH ; cylinder
|
||||
shl ah, 6 ; * 32
|
||||
or cl, ah ; cylinder
|
||||
|
||||
pop ax
|
||||
mov dl,al
|
||||
pop ax
|
||||
|
||||
RET
|
||||
ret
|
||||
|
||||
; int 13h/ah = 02h read disk sectors into memory
|
||||
; al = number of sectors to read
|
||||
; ch = cylinder number
|
||||
; cl = sector number
|
||||
; dh = head number
|
||||
; dl = drive number
|
||||
; es:bx = points to data buffer
|
||||
disk_read:
|
||||
push ax
|
||||
push bx
|
||||
@@ -209,22 +253,32 @@ disk_read:
|
||||
push di
|
||||
|
||||
|
||||
call lba_to_chs
|
||||
; cl = sector
|
||||
; ch = cylinder
|
||||
; dh = head
|
||||
; dl = drive
|
||||
call lba_to_chs ; Get the chs address
|
||||
|
||||
mov ah, 02h
|
||||
mov ah, 02h ; BIOS ah code for read disk sectors to memory
|
||||
|
||||
; repeat drive read 3 times (incase of random error)
|
||||
mov di, 3 ; counter
|
||||
|
||||
; This is basically
|
||||
; for di in range(3,1,1):
|
||||
; We test to see if we can read the disk 3 times
|
||||
retry:
|
||||
stc
|
||||
; When the disk is tried to be read, the CF flag will be
|
||||
; set if there's an error, so we just clear it to return it to
|
||||
; the default state
|
||||
stc ; sets the cf flag
|
||||
int 13h
|
||||
jnc done_read
|
||||
jnc done_read ; jump if cf = 0
|
||||
|
||||
call disk_reset ; Reset drivers of disk
|
||||
|
||||
dec di
|
||||
test di, di
|
||||
dec di ; di -= 1
|
||||
test di, di ; if di = 0
|
||||
jnz retry
|
||||
|
||||
fail_disk_read:
|
||||
@@ -233,6 +287,7 @@ fail_disk_read:
|
||||
hlt
|
||||
jmp halt
|
||||
|
||||
; int 13h / ah = 00h reset disk system
|
||||
disk_reset:
|
||||
pusha
|
||||
mov ah, 0 ; Reset drive
|
||||
@@ -280,14 +335,15 @@ print_string:
|
||||
pop si
|
||||
ret
|
||||
|
||||
boot_text: db '[OK] Boot sequence begun...', 0
|
||||
kernel_found_text: db '[OK] Kernel located...', 0
|
||||
disk_read_fail: db '[ERR] Failed to read disk!', 0
|
||||
boot_text: db 'OK] Boot begun', 0
|
||||
kernel_found_text: db 'OK] Kernel located', 0
|
||||
kernel_loading: db 'OK] Loading kernel', 0
|
||||
disk_read_fail: db 'ERR] Disk read fail', 0
|
||||
file_kernel_bin: db 'KERNEL BIN' ; Must have a double space between KERNEL and BIN
|
||||
kernel_load_error: db '[ERR] Kernel not found!', 0
|
||||
kernel_load_error: db 'ERR] Kernel not found', 0
|
||||
|
||||
kernel_cluster: dw 0
|
||||
kernel_load_segment: equ 2000h
|
||||
kernel_load_segment: equ 2000h ; an area in memory we know should be available
|
||||
kernel_load_offset: equ 0
|
||||
|
||||
|
||||
|
||||
58
source/kernel/data.asm
Normal file
58
source/kernel/data.asm
Normal file
@@ -0,0 +1,58 @@
|
||||
; Define Fat12 header
|
||||
bdb_oem: db 'MSWIN4.1' ; ignore
|
||||
bdb_bytes_per_sector: dw 200h ; = 512d
|
||||
bdb_sectors_per_cluster: db 01h ; sector = cluster
|
||||
bdb_reserved_sectors: dw 01h
|
||||
bdb_fat_count: db 02h ; We've got a fat1 and fat2
|
||||
bdb_dir_entries_count: dw 0E0h ; Maximum number of root directory entries
|
||||
bdb_total_sectors: dw 0B40h ; = 2880d
|
||||
bdb_media_descriptor_type: db 0F0h ; ignore
|
||||
bdb_sectors_per_fat: dw 09h
|
||||
bdb_sectors_per_track: dw 12h ; = 18d
|
||||
bdb_number_of_heads: dw 02h ; top and bottom of the floppy disk
|
||||
bdb_hidden_sectors: dd 0 ; ignore
|
||||
bdb_large_sector_count: dd 0 ; total sector count for fat32 (0 for fat12 or fat16)
|
||||
|
||||
; extended boot record
|
||||
ebr_drive_number: db 0 ; ignore
|
||||
db 0 ; ignore
|
||||
ebr_signature: db 29h ; boot signature, indicates that the next three fields are present (0x29)
|
||||
ebr_volume_id: db 12h, 34h, 56h, 78h ; unique id for volume tracking
|
||||
ebr_volume_label: db 'CRAWOS0.0.6' ; must be 11 bytes
|
||||
ebr_system_id: db 'FAT12 ' ; must be 8 bytes
|
||||
|
||||
|
||||
; String operations
|
||||
fat12_file_name: db ' ' ; 11 free bytes to store a filename in
|
||||
boot_message: db 'OK] Kernel successfully loaded!\n"HELP" to see a list of available commands\n', 0
|
||||
user_input: times 20 db 0
|
||||
prompt_length: dw 20
|
||||
prompt: db 'sh > ', 0
|
||||
help_string: db 'HELP', 0
|
||||
clear_string: db 'CLEAR', 0
|
||||
reboot_string: db 'REBOOT', 0
|
||||
basic_string: db 'BAS', 0
|
||||
cat_string: db 'CAT', 0
|
||||
ls_string: db 'LS', 0
|
||||
ed_string: db 'ED', 0
|
||||
help_text: db 'This is for Cowards:\n"LS" to list the directory,\n"CAT <filename>" to output the contents of a file,\n"BAS <filename>" to run a basic script,\n"HELP" for this helpful text,\n"CLEAR" to clear the screen,\n"REBOOT" or esc to reboot\n', 0
|
||||
basic_text: db 'BASIC PROGRAM BEGUN:\n', 0
|
||||
command_result_text: db 'You typed: ', 0
|
||||
unknown_command: db 'Error: Unkown Command..\n ', 0
|
||||
stringified_int: db 0,0,0,0,0,0 ; Can store up to 6 digits
|
||||
|
||||
int_tmp: dw 0
|
||||
; Disk operations
|
||||
disk_read_fail: db 'Error: Could not read disk\n', 0
|
||||
file_not_found: db 'File not found\n', 0
|
||||
too_long_filename: db 'Filename too long for Fat12\n', 0
|
||||
file_found: db 'File found\n', 0
|
||||
loading_root: db 'Loading root diretory\n', 0
|
||||
file_name_length: db 0
|
||||
file_cluster: dw 0
|
||||
file_length: dd 0
|
||||
read_write_flag: db 02
|
||||
empty_byte: db 0
|
||||
empty_word: dw 0
|
||||
empty_dword: dd 0
|
||||
tmp: db '97', 0
|
||||
File diff suppressed because it is too large
Load Diff
29
source/kernel/features/check.asm
Normal file
29
source/kernel/features/check.asm
Normal file
@@ -0,0 +1,29 @@
|
||||
check_is_number:
|
||||
cmp al, 48
|
||||
jl .not_number
|
||||
cmp al, 57
|
||||
jg .not_number
|
||||
stc
|
||||
ret
|
||||
.not_number:
|
||||
clc
|
||||
ret
|
||||
|
||||
check_is_letter:
|
||||
cmp al, 65
|
||||
jl .not_letter
|
||||
cmp al, 122
|
||||
jg .not_letter
|
||||
cmp al, 90
|
||||
jg .maybe_not_letter
|
||||
stc
|
||||
ret
|
||||
.maybe_not_letter:
|
||||
cmp al, 97
|
||||
jl .not_letter
|
||||
stc
|
||||
ret
|
||||
.not_letter:
|
||||
clc
|
||||
ret
|
||||
|
||||
90
source/kernel/features/cli.asm
Executable file → Normal file
90
source/kernel/features/cli.asm
Executable file → Normal file
@@ -1,50 +1,80 @@
|
||||
os_start_cli:
|
||||
pusha
|
||||
|
||||
|
||||
call os_print_newline
|
||||
|
||||
mov si, prompt
|
||||
call os_print_string
|
||||
|
||||
mov ax, 20
|
||||
mov bx, 20
|
||||
|
||||
mov di, user_input
|
||||
mov ax, user_input
|
||||
|
||||
call os_display_input
|
||||
call keyboard_display_input
|
||||
jc power_reboot
|
||||
jmp os_read_cli
|
||||
|
||||
|
||||
; ------------------------------------------------
|
||||
|
||||
os_read_cli:
|
||||
pusha
|
||||
mov ax, user_input
|
||||
call string_upper_case ; Make the input uppercase so it's case insensitive
|
||||
|
||||
.output_the_user_input:
|
||||
call os_print_newline
|
||||
|
||||
.check_matches: ; Check if the user input matches any internal commands
|
||||
; Help
|
||||
mov si, user_input
|
||||
mov di, help_string
|
||||
call os_compare_strings
|
||||
cmp cl, 1
|
||||
je help
|
||||
je .help
|
||||
|
||||
; Clear screen
|
||||
mov si, user_input
|
||||
mov di, clear_string
|
||||
call os_compare_strings
|
||||
cmp cl, 1
|
||||
je clear
|
||||
je .clear
|
||||
|
||||
; Reboot
|
||||
mov si, user_input
|
||||
mov di, pong_string
|
||||
mov di, reboot_string
|
||||
call os_compare_strings
|
||||
cmp cl, 1
|
||||
je pong
|
||||
je power_reboot
|
||||
|
||||
; Basic
|
||||
mov si, user_input
|
||||
mov di, snake_string
|
||||
mov di, basic_string
|
||||
call os_compare_strings
|
||||
cmp cl, 1
|
||||
je snake
|
||||
je .basic
|
||||
|
||||
; Cat
|
||||
mov si, user_input
|
||||
mov di, cat_string
|
||||
call os_compare_strings
|
||||
cmp cl, 1
|
||||
je .cat
|
||||
|
||||
; LS
|
||||
mov si, user_input
|
||||
mov di, ls_string
|
||||
call os_compare_strings
|
||||
cmp cl, 1
|
||||
je .ls
|
||||
|
||||
;ED
|
||||
mov si, user_input
|
||||
mov di, ed_string
|
||||
call os_compare_strings
|
||||
cmp cl, 1
|
||||
je .ed
|
||||
|
||||
jmp .unkown
|
||||
|
||||
@@ -52,39 +82,37 @@ os_read_cli:
|
||||
mov si, unknown_command
|
||||
call os_print_string
|
||||
mov si, user_input
|
||||
call os_print_string_nl
|
||||
call os_print_string
|
||||
jmp .finish
|
||||
|
||||
.finish:
|
||||
; Clear the user input
|
||||
mov al, 0
|
||||
mov cx, 20
|
||||
mov di,user_input
|
||||
repe stosb
|
||||
popa
|
||||
call os_start_cli
|
||||
|
||||
clear:
|
||||
.clear:
|
||||
call os_set_text_mode
|
||||
call os_read_cli.finish
|
||||
|
||||
help:
|
||||
.help:
|
||||
mov si, help_text
|
||||
call os_print_string_nl
|
||||
call os_print_string
|
||||
call os_read_cli.finish
|
||||
|
||||
pong:
|
||||
call game_pong
|
||||
.basic:
|
||||
call util_basic
|
||||
call os_read_cli.finish
|
||||
.cat:
|
||||
call util_cat
|
||||
call os_read_cli.finish
|
||||
.ls:
|
||||
call util_ls
|
||||
call os_read_cli.finish
|
||||
.ed:
|
||||
call util_ed
|
||||
call os_read_cli.finish
|
||||
|
||||
snake:
|
||||
call game_snake
|
||||
call os_read_cli.finish
|
||||
|
||||
section .data
|
||||
welcome_text db 'Welcome to CrawOS, the Cool, Real and AWsome Operating System', 0
|
||||
user_input times 20 db 0
|
||||
prompt_length db 20
|
||||
prompt db 'CrawOS sh> ', 0
|
||||
help_string db 'HELP', 0
|
||||
clear_string db 'CLEAR', 0
|
||||
pong_string db 'PONG', 0
|
||||
snake_string db 'SNAKE', 0
|
||||
help_text db 'This is for Cowards: "HELP" for this help text, "CLEAR" to clear the screen, esc to reboot', 0
|
||||
command_result_text db 'You typed: ', 0
|
||||
unknown_command db 'Error: Unkown Command.. ', 0
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
; Reset the disk system using int 13h / AH = 00h
|
||||
disk_reset:
|
||||
pusha
|
||||
stc
|
||||
mov ah, 00h
|
||||
int 13h
|
||||
popa
|
||||
ret
|
||||
|
||||
; ------------------------------------
|
||||
|
||||
; Load the root directory into memory (into disk_buffer which is 24000h)
|
||||
disk_load_root:
|
||||
pusha
|
||||
mov ax, 19 ; First sector of root entry
|
||||
call math_lba_to_chs ; Assigns ch, cl and dh the correct values
|
||||
xor ax,ax ; clear ax so I can use it on next line
|
||||
mov dl, [ebr_drive_number] ; Drive number
|
||||
mov ah, 02h ; BIOS ah code for read disk sectors to memory
|
||||
mov al, 0Ch ; Root directory has 12 sectors
|
||||
|
||||
mov si, root_buffer ; ES:BX should point to our buffer
|
||||
mov bx, si
|
||||
|
||||
; repeat drive read 3 times (incase of random error)
|
||||
mov di, 3 ; counter
|
||||
; This is basically
|
||||
; for di in range(3,1,1):
|
||||
; We test to see if we can read the disk 3 times
|
||||
.try_read_disk: ; Try read the floppy three times
|
||||
; When the disk is tried to be read, the CF flag will be
|
||||
; set if there's an error, so we just clear it to return it to
|
||||
; the default state
|
||||
stc ; sets the cf flag
|
||||
int 13h
|
||||
jnc .done_read ; jump if cf = 0
|
||||
|
||||
call disk_reset ; Reset drivers of disk
|
||||
|
||||
dec di ; di -= 1
|
||||
test di, di ; if di = 0
|
||||
jnz .try_read_disk
|
||||
jmp .disk_error
|
||||
.disk_error:
|
||||
mov si, disk_read_fail
|
||||
call os_print_string
|
||||
popa
|
||||
ret
|
||||
.done_read:
|
||||
popa
|
||||
ret
|
||||
|
||||
; ---------------------------------------------
|
||||
|
||||
; Reads a certain number of sectors into memory
|
||||
; IN
|
||||
; ax = LBA
|
||||
; es:bx = area to read to/write from
|
||||
; [read_write_flag] = 02 or 03 for read or write
|
||||
; dl = ebr drive number
|
||||
; int 13h/ah = 02h/03h read/write disk sectors into memory
|
||||
; al = number of sectors to read
|
||||
; ah = read/write 02=read, 03=write IMPORTANT
|
||||
; ch = cylinder number
|
||||
; cl = sector number
|
||||
; dh = head number
|
||||
; dl = drive number
|
||||
; es:bx = points to data buffer
|
||||
disk_read_or_write:
|
||||
push ax
|
||||
push bx
|
||||
push cx
|
||||
push dx
|
||||
push di
|
||||
|
||||
; cl = sector
|
||||
; ch = cylinder
|
||||
; dh = head
|
||||
; dl = drive
|
||||
call math_lba_to_chs ; Get the chs address
|
||||
|
||||
mov ah, [read_write_flag]
|
||||
mov al, 1
|
||||
|
||||
; repeat drive read 3 times (incase of random error)
|
||||
mov di, 3 ; counter
|
||||
|
||||
; This is basically
|
||||
; for di in range(3,1,1):
|
||||
; We test to see if we can read the disk 3 times
|
||||
.retry:
|
||||
; When the disk is tried to be read, the CF flag will be
|
||||
; set if there's an error, so we just clear it to return it to
|
||||
; the default state
|
||||
stc ; sets the cf flag
|
||||
int 13h
|
||||
jnc .done_read ; jump if cf = 0
|
||||
|
||||
call .disk_reset ; Reset drivers of disk
|
||||
|
||||
dec di ; di -= 1
|
||||
test di, di ; if di = 0
|
||||
jnz .retry
|
||||
|
||||
.fail_disk_read:
|
||||
pop di
|
||||
pop dx
|
||||
pop cx
|
||||
pop bx
|
||||
pop ax
|
||||
ret
|
||||
|
||||
; int 13h / ah = 00h reset disk system
|
||||
.disk_reset:
|
||||
pusha
|
||||
mov ah, 0 ; Reset drive
|
||||
stc
|
||||
int 13h
|
||||
jc .fail_disk_read
|
||||
popa
|
||||
ret
|
||||
|
||||
.done_read:
|
||||
pop di
|
||||
pop dx
|
||||
pop cx
|
||||
pop bx
|
||||
pop ax
|
||||
ret
|
||||
|
||||
|
||||
; -------------------------------------
|
||||
; CLEAR DATA
|
||||
; Uses the metadata buffer to determine the file size in the data buffer and clears it.
|
||||
disk_clear_file_buffer:
|
||||
pusha
|
||||
|
||||
mov si, metadata_buffer
|
||||
mov cx, [si+28] ; MOve the filelength into the ax
|
||||
shr cx, 1 ; Divide by 4 because we'll write over using double words
|
||||
mov si, empty_word
|
||||
lodsb ; Load the empty dword
|
||||
mov di, file_buffer
|
||||
.loop:
|
||||
stosb
|
||||
dec cx
|
||||
cmp cx, 0
|
||||
jbe .loop
|
||||
.finish:
|
||||
popa
|
||||
ret
|
||||
disk_clear_output_buffer:
|
||||
pusha
|
||||
mov cx, 0x7f0 ; Length of the output buffer
|
||||
mov si, empty_word
|
||||
lodsb ; Load the empty word into ax
|
||||
mov di, output_buffer
|
||||
.loop:
|
||||
stosb ; Store empty dword in di
|
||||
dec cx
|
||||
cmp cx, 0
|
||||
ja .loop
|
||||
.finish:
|
||||
popa
|
||||
ret
|
||||
|
||||
; -------------------------------------
|
||||
; IN
|
||||
; si: filename
|
||||
|
||||
; OUT
|
||||
; data_buffer: file contents
|
||||
; TODO use predefined data for calculations
|
||||
; CX = 0 if failed to load
|
||||
disk_load_file:
|
||||
pusha
|
||||
call string_length ; cl = string length
|
||||
cmp cl, 11
|
||||
ja .filename_too_long
|
||||
|
||||
mov di, fat12_file_name
|
||||
call os_format_fat_filename
|
||||
|
||||
; Prepare values
|
||||
mov si, root_buffer
|
||||
xor bx,bx
|
||||
.search_root:
|
||||
mov di, fat12_file_name ; Move the name of the kernel into di
|
||||
mov cx, 11 ; length of filenames in fat12
|
||||
push si ; Preserve si
|
||||
repe cmpsb ; Repeat a comparison of bytes between file name and current bytes until it finds a match
|
||||
pop si ; Retrieve si
|
||||
je .found_file
|
||||
|
||||
add si, 32 ; increment di to the next directory entry
|
||||
inc bx
|
||||
cmp bx, [bdb_dir_entries_count] ; Have we reached the number of directories that exist
|
||||
jl .search_root ; Repeat search
|
||||
|
||||
jmp .file_not_found ; If the last dir has been searched, then there is no kernel
|
||||
.found_file:
|
||||
call disk_clear_file_buffer
|
||||
mov di, metadata_buffer
|
||||
mov cx, 32
|
||||
.write_metadata:
|
||||
; Write data to metadata buffer
|
||||
lodsb
|
||||
stosb
|
||||
sub cx, 1
|
||||
cmp cx, 0
|
||||
jne .write_metadata
|
||||
.read_kernel:
|
||||
sub si, 32
|
||||
mov ax, [si+28] ; File length
|
||||
mov [file_length], ax
|
||||
mov ax, [si+26] ; ax is now a pointer to the pointer to the file relative to the data segments :D
|
||||
add ax, 1Fh
|
||||
mov [file_cluster], ax
|
||||
; Setup registers for disk read
|
||||
mov si, file_buffer
|
||||
mov bx, si
|
||||
mov dl, [ebr_drive_number]
|
||||
|
||||
mov ch, 02h
|
||||
mov [read_write_flag], ch ; READ
|
||||
call disk_read_or_write ; Load file from disk into memory
|
||||
jmp .done
|
||||
.file_not_found:
|
||||
mov si, file_not_found
|
||||
call os_print_string
|
||||
jmp .done_fail
|
||||
.filename_too_long:
|
||||
mov si, too_long_filename
|
||||
call os_print_string
|
||||
jmp .done_fail
|
||||
.done_fail:
|
||||
popa
|
||||
mov bx, 1
|
||||
ret
|
||||
.done:
|
||||
popa
|
||||
xor bx,bx
|
||||
ret
|
||||
|
||||
; Write data to file
|
||||
; takes the contents in the data buffer for a fixed
|
||||
; number of characters definied by [file_length]
|
||||
; and write it to the file that si points to
|
||||
; It also must edit the fat entry data
|
||||
disk_write_file:
|
||||
pusha
|
||||
; Check if file name is too long
|
||||
call string_length ; cl = string length
|
||||
cmp cl, 11
|
||||
ja .filename_too_long
|
||||
; Convert file name to a fat filename
|
||||
mov di, fat12_file_name
|
||||
call os_format_fat_filename
|
||||
|
||||
; Locate the file entry in memory
|
||||
; Prepare values
|
||||
mov si, root_buffer
|
||||
xor bx,bx
|
||||
.search_root:
|
||||
mov di, fat12_file_name ; Move the name of the kernel into di
|
||||
mov cx, 11 ; length of filenames in fat12
|
||||
push si ; Preserve si
|
||||
repe cmpsb ; Repeat a comparison of bytes between file name and current bytes until it finds a match
|
||||
pop si ; Retrieve si
|
||||
je .found_file
|
||||
|
||||
add si, 32 ; increment di to the next directory entry
|
||||
inc bx
|
||||
cmp bx, [bdb_dir_entries_count] ; Have we reached the number of directories that exist
|
||||
jl .search_root ; Repeat search
|
||||
|
||||
jmp .file_not_found ; If the last dir has been searched, then there is no kernel
|
||||
.found_file:
|
||||
; Find where the file is on the disk
|
||||
mov ax, [si+28] ; File length entry
|
||||
mov [file_length], ax
|
||||
mov ax, [si+26] ; ax is now a pointer to the pointer to the file relative to the data segments :D
|
||||
add ax, 1Fh
|
||||
mov [file_cluster], ax
|
||||
; Setup registers for disk read
|
||||
mov si, file_buffer
|
||||
mov bx, si
|
||||
mov dl, [ebr_drive_number]
|
||||
|
||||
mov ch, 03h
|
||||
mov [read_write_flag], ch ; WRITE
|
||||
call disk_read_or_write ; Load file from disk into memory
|
||||
jmp .finish
|
||||
; DI now points to a fat12 formatted file name
|
||||
|
||||
; Write to those sectors
|
||||
; Update FAT entry
|
||||
.file_not_found: ; TODO create a file if it's not found
|
||||
mov si, file_not_found
|
||||
call os_print_string
|
||||
jmp .finish
|
||||
.filename_too_long:
|
||||
mov si, too_long_filename
|
||||
call os_print_string
|
||||
jmp .finish
|
||||
.finish:
|
||||
popa
|
||||
ret
|
||||
|
||||
; TODO support long file names
|
||||
; TODO don't work twice
|
||||
; Store a list of the files in file_buffer
|
||||
; in a human readable format
|
||||
; OUT
|
||||
; output_buffer: the list (string)
|
||||
disk_list_contents:
|
||||
call disk_load_root
|
||||
pusha
|
||||
mov si, root_buffer
|
||||
mov di, output_buffer
|
||||
.loop:
|
||||
call string_unformat_fat_filename
|
||||
mov cx, 000Ch
|
||||
sub cx, [file_name_length]
|
||||
add di, [file_name_length]
|
||||
mov al, 20h
|
||||
.space_loop:
|
||||
stosb
|
||||
dec cx
|
||||
cmp cx, 0
|
||||
jne .space_loop
|
||||
mov ax, [si+28d]
|
||||
call string_cast_from_int
|
||||
push si
|
||||
mov si, stringified_int
|
||||
lodsb
|
||||
.output_file_size_loop:
|
||||
stosb
|
||||
lodsb
|
||||
cmp al, 0
|
||||
jne .output_file_size_loop
|
||||
.after_fs_loop:
|
||||
pop si
|
||||
mov al, 0Ah
|
||||
stosb
|
||||
|
||||
add si, 20h
|
||||
lodsb ; +1
|
||||
dec si ; -1
|
||||
cmp al, 0 ; You've come to the end of the root entries
|
||||
je .finish
|
||||
cmp al, 00E5h ; E5h is a marker that the Fat entry is available
|
||||
je .finish
|
||||
jmp .loop
|
||||
.finish:
|
||||
mov cx, output_buffer
|
||||
sub di, cx
|
||||
mov [file_length], di
|
||||
stosb
|
||||
popa
|
||||
ret
|
||||
|
||||
disk_file_list:
|
||||
ret
|
||||
disk_file_exists:
|
||||
ret
|
||||
disk_file_size:
|
||||
ret
|
||||
disk_remove_file:
|
||||
ret
|
||||
disk_rename_file:
|
||||
ret
|
||||
|
||||
@@ -51,3 +51,10 @@ section .data
|
||||
y_end dw 0
|
||||
colour db 1111b
|
||||
|
||||
graphics_dialogue_box:
|
||||
ret
|
||||
|
||||
graphics_file_selector:
|
||||
ret
|
||||
graphics_list_dialogue:
|
||||
ret
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
; AX = key pressed
|
||||
; Waits until key is pressed before returning
|
||||
os_read_input:
|
||||
mov ah, 11h ; BIOS call to check for key
|
||||
int 16h ; Interrupt
|
||||
@@ -12,16 +13,28 @@ os_read_input:
|
||||
int 16h
|
||||
ret
|
||||
|
||||
; AX = key pressed
|
||||
; Returns straight away
|
||||
; carry flag is set when escape is pressed and data is returned straight away
|
||||
keyboard_check_key:
|
||||
xor ax,ax
|
||||
mov ah, 11h ; BIOS call to check for key
|
||||
int 16h ; Interrupt
|
||||
jz .no_key
|
||||
.no_key:
|
||||
xor ax,ax
|
||||
ret
|
||||
; -------------------------------------------
|
||||
|
||||
os_display_input:
|
||||
; IN:
|
||||
; AX = output address
|
||||
; BX = max length
|
||||
; OUT:
|
||||
; BX = 1 if escape is pressed
|
||||
keyboard_display_input:
|
||||
pusha
|
||||
mov cx, [prompt_length]
|
||||
|
||||
.loop:
|
||||
call .check_key_pressed
|
||||
|
||||
jmp .loop
|
||||
mov di, ax
|
||||
xor ax,ax
|
||||
mov [prompt_length], bx
|
||||
|
||||
.check_key_pressed:
|
||||
call os_read_input
|
||||
@@ -35,25 +48,30 @@ os_display_input:
|
||||
cmp al, 1Bh
|
||||
je .esc_key
|
||||
|
||||
cmp cx, 0
|
||||
jb .check_key_pressed
|
||||
dec bx
|
||||
|
||||
cmp bx, 0
|
||||
je .enter_key ; Once the limit is reached just enter it
|
||||
ja .print_current_input ; Echo the user input back
|
||||
|
||||
call .print_current_input
|
||||
dec cx
|
||||
jmp .check_key_pressed
|
||||
|
||||
.esc_key:
|
||||
call os_reboot
|
||||
popa
|
||||
mov bx, 1
|
||||
ret
|
||||
|
||||
.enter_key:
|
||||
mov al, 0
|
||||
stosb
|
||||
popa
|
||||
call os_read_cli
|
||||
xor bx,bx
|
||||
ret ; Return to the parent function (whatever that may be)
|
||||
|
||||
.backspace:
|
||||
call .move_cursor_back ; then .move_cursor_back
|
||||
call .loop ; Else .loop
|
||||
cmp bx, [prompt_length] ; Cannot backspace if the cursor is at the start
|
||||
jb .move_cursor_back ; then .move_cursor_back
|
||||
jmp .check_key_pressed ; Else check the next key
|
||||
|
||||
.move_cursor_back:
|
||||
mov ah, 0Eh
|
||||
@@ -65,13 +83,24 @@ os_display_input:
|
||||
mov al, 08h
|
||||
int 10h
|
||||
|
||||
inc bx
|
||||
dec di
|
||||
jmp os_display_input
|
||||
jmp .check_key_pressed
|
||||
|
||||
.print_current_input:
|
||||
.print_current_input: ; Echo back that character and return the key inputing
|
||||
stosb
|
||||
|
||||
mov ah, 0Eh
|
||||
int 10h
|
||||
jmp .check_key_pressed
|
||||
.input_length db 0
|
||||
|
||||
keyboard_get_cursor_pos:
|
||||
ret
|
||||
keyboard_wait_for_key:
|
||||
ret
|
||||
keyboard_show_cursor:
|
||||
ret
|
||||
keyboard_hide_cursor:
|
||||
ret
|
||||
keyboard_move_cursor:
|
||||
ret
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
; LBA = index of data segment on disk
|
||||
; CHS = cylinder, header, sector
|
||||
; T = LBA/sectors per track
|
||||
; S = (LBA%sectors per track) + 1
|
||||
; H = T % heads
|
||||
; C = T / headers
|
||||
; input, LBA index: ax
|
||||
; ouput:
|
||||
; sector number: cl
|
||||
; cylinder: ch
|
||||
; head: dh
|
||||
; Example where LBA = 50h (CHS = 2,0,9)
|
||||
; ax = 0050h, push this to the stack
|
||||
; dx = 0000h
|
||||
; dx = 50h % 12h = 0008h
|
||||
; ax = 50h / 12h = 0004h
|
||||
; dx = 0009h
|
||||
; cx = 0009h
|
||||
; dx = 0000h
|
||||
; dx = 04h % 02h = 0000h
|
||||
; ax = 04h / 02h = 0002h
|
||||
; dh = 00h (dx = 0000h)
|
||||
; ch = 02h (cx = 0209h)
|
||||
; ah = 00h (ax = 0002h)
|
||||
; cl = 09h OR 00h = 09h (cx = 0209h)
|
||||
; ax = 0050h
|
||||
; dl = 50h (dx = 0050h)
|
||||
; ax = 0050h
|
||||
; thus:
|
||||
; cylinder (ch) = 02h
|
||||
; head (cl) = 00h
|
||||
; sector (dh) = 09h
|
||||
math_lba_to_chs:
|
||||
push ax
|
||||
push dx
|
||||
|
||||
xor dx,dx ; clear dx
|
||||
div word [bdb_sectors_per_track] ; (LBA % sectors per track) + 1 = sector
|
||||
inc dx ; sector, dx stores the remainder so we increment that.
|
||||
mov cx,dx
|
||||
|
||||
xor dx,dx ; clear dx
|
||||
div word [bdb_number_of_heads]
|
||||
mov dh,dl ; head, dx stores remainder so we move that up 8 bits to dh
|
||||
|
||||
mov ch,al
|
||||
shl ah, 6 ; * 32
|
||||
or cl, ah ; cylinder
|
||||
|
||||
pop ax
|
||||
mov dl,al
|
||||
pop ax
|
||||
|
||||
ret
|
||||
math_get_random:
|
||||
ret
|
||||
|
||||
4
source/kernel/features/misc.asm
Normal file
4
source/kernel/features/misc.asm
Normal file
@@ -0,0 +1,4 @@
|
||||
misc_pause:
|
||||
ret
|
||||
misc_get_api_version:
|
||||
ret
|
||||
23
source/kernel/features/ports.asm
Normal file
23
source/kernel/features/ports.asm
Normal file
@@ -0,0 +1,23 @@
|
||||
; IN:
|
||||
; DX = port address
|
||||
; AL = byte
|
||||
port_byte_out:
|
||||
pusha
|
||||
out dx,al
|
||||
popa
|
||||
ret
|
||||
; -----------------------------------------
|
||||
; IN:
|
||||
; DX = port address
|
||||
; OUT:
|
||||
; AL = byte from port
|
||||
port_byte_in:
|
||||
in al, dx
|
||||
ret
|
||||
|
||||
port_serial_enable:
|
||||
ret
|
||||
port_send_via_serial:
|
||||
ret
|
||||
port_get_via_serial:
|
||||
ret
|
||||
5
source/kernel/features/power.asm
Executable file → Normal file
5
source/kernel/features/power.asm
Executable file → Normal file
@@ -1,6 +1,9 @@
|
||||
os_reboot:
|
||||
power_reboot:
|
||||
mov ax, 0x5307
|
||||
mov bx, 0x0001
|
||||
mov cx, 0x0003
|
||||
int 0x19
|
||||
|
||||
; Not done
|
||||
power_shutdown:
|
||||
ret
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
sound_speaker_tone:
|
||||
ret
|
||||
sound_speaker_off:
|
||||
ret
|
||||
|
||||
324
source/kernel/features/strings.asm
Executable file → Normal file
324
source/kernel/features/strings.asm
Executable file → Normal file
@@ -6,21 +6,337 @@
|
||||
; memory address 2: |s|n|a|k|e|0|
|
||||
; ^
|
||||
; SI => lodsb loads value stored at SI to AX and then increments SI if DF is 0
|
||||
; Additionaly, if the di string ends and the relevant character in si contains a space, it will still return
|
||||
; the strings as being equal, this is to allow for command line arquments
|
||||
os_compare_strings:
|
||||
cld
|
||||
|
||||
.compare:
|
||||
mov al, 0
|
||||
scasb
|
||||
je .di_ended
|
||||
dec di
|
||||
lodsb
|
||||
scasb ; Compare di to si
|
||||
jne .unequal ; If they are no equal, jump to .unequal
|
||||
cmp al, 0 ; Check if string is finished
|
||||
je .equal ; If it has, return true
|
||||
jne .unequal ; If they are not equal, jump to .unequal
|
||||
jmp .compare ; Finally, repeat
|
||||
|
||||
.unequal:
|
||||
mov cl, 0 ; Change to 0 if unquality is proven
|
||||
ret
|
||||
|
||||
.di_ended:
|
||||
lodsb
|
||||
cmp al, 20h ; 20h = space
|
||||
je .equal
|
||||
cmp al, 0
|
||||
je .equal
|
||||
jmp .unequal
|
||||
.equal:
|
||||
mov cl, 1
|
||||
ret
|
||||
|
||||
; ------------------------------------------------------------------
|
||||
; os_string_compare -- See if two strings match
|
||||
; IN: SI = string one, DI = string two
|
||||
; OUT: carry set if same, clear if different
|
||||
; from MikeOS kernel
|
||||
|
||||
string_direct_compare:
|
||||
pusha
|
||||
.more:
|
||||
mov al, [si] ; Retrieve string contents
|
||||
mov bl, [di]
|
||||
cmp al, bl ; Compare characters at current location
|
||||
jne .not_same
|
||||
cmp al, 0 ; End of first string? Must also be end of second
|
||||
je .end
|
||||
inc si
|
||||
inc di
|
||||
jmp .more
|
||||
.not_same: ; If unequal lengths with same beginning, the byte
|
||||
popa ; comparison fails at shortest string terminator
|
||||
clc ; Clear carry flag
|
||||
ret
|
||||
.end: ; Both strings terminated at the same position
|
||||
popa
|
||||
stc ; Set carry flag
|
||||
ret
|
||||
|
||||
|
||||
; --------------------------------------------------------------------
|
||||
; Copies a string from si to di
|
||||
; IN:
|
||||
; si: points to first character of source string
|
||||
; OUT:
|
||||
; di: points to first character of destination string
|
||||
string_copy:
|
||||
pusha
|
||||
.loop:
|
||||
; TODO could this cause issue when a character is > 1 byte?
|
||||
lodsb ; Load si character into ax and increment si by 1
|
||||
stosb ; Store ax in di location and increment di by 1
|
||||
cmp ax, 0 ; if ax is a 0, quit
|
||||
jne .loop
|
||||
.done:
|
||||
popa
|
||||
ret
|
||||
|
||||
; --------------------------------------------------------------------
|
||||
; Joins two strings together into a new string
|
||||
; IN/OUT:
|
||||
; ax: string1
|
||||
; bx: string2
|
||||
; cx: result string
|
||||
|
||||
string_join:
|
||||
pusha
|
||||
.copy_string1:
|
||||
mov si, ax
|
||||
mov di, cx
|
||||
call string_copy
|
||||
mov ax, cx ; TODO check this bit
|
||||
call string_length
|
||||
add cx, ax
|
||||
.copy_string2:
|
||||
mov si, bx
|
||||
mov di, dx
|
||||
call string_copy
|
||||
.done:
|
||||
popa
|
||||
ret
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
; Converts a string to an integer
|
||||
; IN: SI = string location (max 5 chars, up to '65536')
|
||||
; OUT: AX = number, DX = length of int string
|
||||
string_cast_to_int:
|
||||
push cx
|
||||
push bx
|
||||
push dx
|
||||
xor cx,cx
|
||||
.loop:
|
||||
xor ax,ax
|
||||
lodsb
|
||||
|
||||
; Exit if the character is not a number
|
||||
cmp al,'0'
|
||||
jl .finish
|
||||
cmp al,'9'
|
||||
jg .finish
|
||||
|
||||
sub al, '0'
|
||||
mov bx, ax
|
||||
|
||||
mov ax, 10
|
||||
mul cx ; Multiple the current value by 10
|
||||
add ax, bx ; Then add the new digit
|
||||
mov cx, ax
|
||||
jmp .loop
|
||||
.finish:
|
||||
mov ax,cx
|
||||
pop dx
|
||||
pop bx
|
||||
pop cx
|
||||
ret
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
; IN: AX = integer (unsigned)
|
||||
; OUT: AX -> null-terminated string
|
||||
string_cast_from_int:
|
||||
pusha
|
||||
cld ; Write backwards
|
||||
mov di, stringified_int
|
||||
|
||||
mov bx, 10 ; base
|
||||
mov cx, 0 ; digit count
|
||||
|
||||
.convert_loop:
|
||||
xor dx, dx
|
||||
div bx ; AX = AX / base, DX = remainder
|
||||
add dl, '0'
|
||||
push dx ; store digit
|
||||
inc cx
|
||||
test ax, ax
|
||||
jnz .convert_loop
|
||||
|
||||
.write_loop:
|
||||
pop ax
|
||||
stosb
|
||||
loop .write_loop
|
||||
|
||||
mov al, 0
|
||||
stosb ; null terminator
|
||||
|
||||
popa
|
||||
mov ax, stringified_int
|
||||
ret
|
||||
|
||||
;----------------------------------------------------------------------------
|
||||
|
||||
; Get the length of a string
|
||||
; 'hello world', 0 is 11 characters long (excluding the terminator)
|
||||
; input: ax points to the string to be counted
|
||||
; output: ax holds the length
|
||||
string_length:
|
||||
push si
|
||||
push cx
|
||||
mov si, ax
|
||||
xor ax,ax ; Clear the cx register
|
||||
xor cx,cx
|
||||
.loop:
|
||||
lodsb
|
||||
cmp al, 0
|
||||
je .finish
|
||||
inc cx
|
||||
jmp .loop
|
||||
.finish:
|
||||
mov ax, cx
|
||||
pop cx
|
||||
pop si
|
||||
ret
|
||||
|
||||
|
||||
|
||||
; convert a string to fat's filename format
|
||||
; It will be capitalised and 11 characters long,
|
||||
; 8 for the filename, 3 for the extension
|
||||
; eg: 'file.txt' -> 'FILE TXT'
|
||||
; input: si points to filename, di points to a free 11 bytes in memory
|
||||
; output: di points to the fat formatted filename
|
||||
os_format_fat_filename:
|
||||
pusha
|
||||
call string_upper_case
|
||||
mov ax, si
|
||||
call string_length ; Stores the length of the string in cl
|
||||
mov cl, al
|
||||
xor ch,ch ; Clear ch to reset it to 0
|
||||
.character_loop:
|
||||
lodsb
|
||||
cmp al, 0
|
||||
je .finish
|
||||
cmp al, 2Eh ; 2Eh
|
||||
je .add_spaces ; This will end up back at .character_loop
|
||||
stosb
|
||||
inc ch
|
||||
jmp .character_loop
|
||||
|
||||
.add_spaces: ; Add the number of spaces as bl holds
|
||||
mov al, ' ' ; 20h = space
|
||||
|
||||
; Work out the number of spaces in between
|
||||
; the name and extension.
|
||||
; 8 - name_length(ch)
|
||||
xor bl, bl
|
||||
sub bl, ch
|
||||
add bl, 7
|
||||
.spaces_loop:
|
||||
stosb
|
||||
cmp bl, 0
|
||||
je .character_loop
|
||||
dec bl
|
||||
jmp .spaces_loop
|
||||
.finish:
|
||||
popa
|
||||
ret
|
||||
|
||||
; Does the inverse of the previous
|
||||
; Converts a fat filename back to human readable
|
||||
; eg. 'KERNEL BIN' -> 'KERNEL.BIN'
|
||||
; input: si points to fat filename (11 bytes)
|
||||
; output:
|
||||
; di points to the unformatted filename
|
||||
; [file_name_length] stores the length of the filename
|
||||
string_unformat_fat_filename:
|
||||
pusha
|
||||
|
||||
xor ax,ax
|
||||
xor dx,dx
|
||||
mov cx, 11 ; Counter
|
||||
.name_loop:
|
||||
lodsb
|
||||
stosb
|
||||
dec cx
|
||||
inc dx
|
||||
cmp cx, 3
|
||||
jne .name_loop
|
||||
push si
|
||||
mov si, di
|
||||
.space_loop:
|
||||
dec si
|
||||
lodsb
|
||||
dec si
|
||||
dec dx
|
||||
cmp al, 20h ; Space
|
||||
je .space_loop
|
||||
jmp .insert_stop
|
||||
.insert_stop:
|
||||
mov di, si
|
||||
inc di
|
||||
inc dx
|
||||
pop si
|
||||
mov al, 2Eh
|
||||
stosb
|
||||
mov cx, 3
|
||||
.extension_loop:
|
||||
lodsb
|
||||
stosb
|
||||
dec cx
|
||||
inc dx
|
||||
cmp cx, 0
|
||||
jne .extension_loop
|
||||
|
||||
.finish:
|
||||
inc dx
|
||||
mov [file_name_length], dx
|
||||
popa
|
||||
ret
|
||||
|
||||
; -------------------------------------------------------------------------------------
|
||||
|
||||
; Convert a string to all upper/lower case
|
||||
; INPUT: ax pointing to a string
|
||||
; OUPUT: the same string in memory will now be capitalised/decapitalised
|
||||
string_upper_case: ; to upper case
|
||||
pusha
|
||||
mov si,ax
|
||||
mov di,si
|
||||
.loop:
|
||||
lodsb ; Load the character into al
|
||||
inc di
|
||||
cmp al, 0
|
||||
je .finish ; If it's null then the string is finished
|
||||
cmp al, 7Ah ; 7Ah = 'z'
|
||||
jns .loop ; Ignore if it's more than 'z'
|
||||
cmp al, 61h ; 61h = 'a'
|
||||
js .loop ; Ignore if it's less than 'a'
|
||||
sub al, 20h ; Otherwise subtract 20h to capitalise it
|
||||
dec di
|
||||
stosb ; Store the new value
|
||||
jmp .loop ; Next character
|
||||
.finish:
|
||||
popa
|
||||
ret
|
||||
|
||||
string_lower_case: ; to lower case
|
||||
pusha
|
||||
mov si, ax
|
||||
mov di, si
|
||||
.loop:
|
||||
lodsb ; Load the character into al
|
||||
inc di
|
||||
cmp al, 0
|
||||
je .finish ; If it's null then the string is finished
|
||||
cmp al, 5Ah ; 5Ah = 'Z'
|
||||
jns .loop ; Ignore if it's more than 'Z'
|
||||
cmp al, 41h ; 41h = 'A'
|
||||
js .loop ; Ignore if it's less than 'A'
|
||||
add al, 20h ; Otherwise subtract 20h to capitalise it
|
||||
dec di
|
||||
stosb ; Store the new value
|
||||
jmp .loop ; Next character
|
||||
.finish:
|
||||
popa
|
||||
ret
|
||||
|
||||
string_print_2hex:
|
||||
ret
|
||||
|
||||
110
source/kernel/features/text.asm
Executable file → Normal file
110
source/kernel/features/text.asm
Executable file → Normal file
@@ -1,4 +1,7 @@
|
||||
; SI = pointer to start of string to be printed
|
||||
; \n for newline
|
||||
; \t for tab
|
||||
; \\ for a single backslash
|
||||
os_print_string:
|
||||
pusha
|
||||
|
||||
@@ -9,33 +12,101 @@ os_print_string:
|
||||
cmp al, 0 ; Compare al to 0
|
||||
je .done ; If char is zero, end of string
|
||||
|
||||
cmp al, 0Ah ; When there's an NL or CR, do both, linux encodes files only with NL
|
||||
je .new_line
|
||||
cmp al, 0Dh
|
||||
je .repeat
|
||||
|
||||
cmp al, 5Ch ; backslash
|
||||
je .backslash
|
||||
|
||||
int 10h ; Otherwise, print it
|
||||
jmp .repeat ; And move on to next char
|
||||
|
||||
.new_line:
|
||||
mov al, 0Ah
|
||||
int 10h
|
||||
mov al, 0Dh
|
||||
int 10h
|
||||
jmp .repeat
|
||||
.backslash:
|
||||
call text_backslash
|
||||
jmp .repeat
|
||||
.done:
|
||||
popa
|
||||
ret
|
||||
|
||||
; Exact same as the above procedure, but this adds a newline
|
||||
; after priting, similar to the difference between Rust's print! and println!
|
||||
os_print_string_nl:
|
||||
; This is similar to the previous, however it prints
|
||||
; raw output (including null) and prints the number
|
||||
; of character defined by cx
|
||||
; IN:
|
||||
; SI = pointer to start of string to be printed
|
||||
; CX = Length of string to print
|
||||
text_print_raw:
|
||||
pusha
|
||||
|
||||
mov ah, 0Eh ; int 10h teletype function, we're telling the BIOS we will print something
|
||||
|
||||
add cx, 1
|
||||
mov ah, 0Eh
|
||||
.repeat:
|
||||
lodsb ; Get char from si into al
|
||||
cmp al, 0 ; Compare al to 0
|
||||
je .done ; If char is zero, end of string
|
||||
|
||||
int 10h ; Otherwise, print it
|
||||
jmp .repeat ; And move on to next char
|
||||
|
||||
.done:
|
||||
call os_print_newline
|
||||
dec cx
|
||||
cmp cx, 0
|
||||
je .finish
|
||||
lodsb
|
||||
cmp al, 00h ; Print a space in place of a Null
|
||||
je .space
|
||||
cmp al, 0Ah ; When there's an NL or CR, do both, linux encodes files only with NL
|
||||
je .new_line
|
||||
cmp al, 09h ; TAB
|
||||
je .tab
|
||||
cmp al, 0Dh
|
||||
je .repeat
|
||||
int 10h
|
||||
jmp .repeat
|
||||
.tab:
|
||||
mov al, ' '
|
||||
int 10h
|
||||
jmp .repeat
|
||||
.space:
|
||||
mov al, 20h
|
||||
int 10h
|
||||
jmp .repeat
|
||||
.new_line:
|
||||
mov al, 0Ah
|
||||
int 10h
|
||||
mov al, 0Dh
|
||||
int 10h
|
||||
jmp .repeat
|
||||
.finish:
|
||||
popa
|
||||
ret
|
||||
|
||||
; NO PUSHING/POPPING!
|
||||
text_backslash: ; If there is a '\', do what it says, \n for newline, \t for tab etc
|
||||
lodsb
|
||||
dec si
|
||||
cmp al, 6Eh ; 'n'
|
||||
je .newline
|
||||
cmp al, 74h ; \t
|
||||
je .tab
|
||||
cmp al, 5Ch ; '\'
|
||||
je .another_backslash
|
||||
ret
|
||||
.newline:
|
||||
mov al, 0Ah ; new line
|
||||
int 10h
|
||||
mov al, 0Dh ; carriage return
|
||||
int 10h
|
||||
jmp .finish_backslash
|
||||
.tab:
|
||||
mov al, 09h ; tab
|
||||
int 10h
|
||||
jmp .finish_backslash
|
||||
.another_backslash: ; This just prints 1 backslash
|
||||
mov al, 5Ch
|
||||
int 10h
|
||||
jmp .finish_backslash
|
||||
.finish_backslash:
|
||||
inc si
|
||||
ret
|
||||
; --------------------------------------------
|
||||
|
||||
os_print_newline:
|
||||
@@ -65,6 +136,15 @@ os_set_text_mode:
|
||||
mov dl, 0
|
||||
int 10h
|
||||
|
||||
text_newline:
|
||||
pusha
|
||||
mov ax,0E0Ah
|
||||
int 10h
|
||||
mov al,0Dh
|
||||
int 10h
|
||||
popa
|
||||
ret
|
||||
|
||||
popa
|
||||
ret
|
||||
|
||||
|
||||
8
source/kernel/features/time.asm
Normal file
8
source/kernel/features/time.asm
Normal file
@@ -0,0 +1,8 @@
|
||||
time_get_time:
|
||||
pusha
|
||||
|
||||
mov ah, 00h
|
||||
int 1Ah
|
||||
|
||||
popa
|
||||
ret
|
||||
61
source/kernel/features/utils.asm
Normal file
61
source/kernel/features/utils.asm
Normal file
@@ -0,0 +1,61 @@
|
||||
util_cat:
|
||||
pusha
|
||||
|
||||
call disk_clear_file_buffer
|
||||
mov si, user_input
|
||||
; TODO make this more consistent to account for double spaces
|
||||
add si, 4 ; Move si to after 'CAT'
|
||||
|
||||
call disk_load_file
|
||||
cmp bx, 1
|
||||
je .done
|
||||
|
||||
mov si, file_buffer
|
||||
mov cx, [file_length]
|
||||
call text_print_raw
|
||||
.done
|
||||
popa
|
||||
ret
|
||||
|
||||
util_ls:
|
||||
pusha
|
||||
|
||||
call disk_list_contents
|
||||
|
||||
mov si, output_buffer
|
||||
call os_print_string
|
||||
call disk_clear_output_buffer
|
||||
|
||||
popa
|
||||
ret
|
||||
|
||||
util_basic:
|
||||
pusha
|
||||
|
||||
call disk_load_root
|
||||
|
||||
; TODO make this more consistent to account for double spaces
|
||||
;add si, 3 ; Move si to after 'BAS'
|
||||
call disk_load_file
|
||||
cmp bx, 1
|
||||
je .done
|
||||
|
||||
mov si, file_buffer
|
||||
|
||||
mov ax, si
|
||||
mov si, 0
|
||||
call basic_run_basic
|
||||
|
||||
.done
|
||||
popa
|
||||
ret
|
||||
|
||||
util_ed:
|
||||
pusha
|
||||
call disk_load_root
|
||||
call disk_load_file
|
||||
|
||||
call ed
|
||||
|
||||
popa
|
||||
ret
|
||||
@@ -48,24 +48,24 @@ game_pong:
|
||||
mov dx, [p1_y]
|
||||
sub dx, 5
|
||||
mov [p1_y], dx
|
||||
jmp .detect_input
|
||||
ret
|
||||
.p1_down:
|
||||
mov dx, [p1_y]
|
||||
add dx, 5
|
||||
mov [p1_y], dx
|
||||
jmp .detect_input
|
||||
ret
|
||||
|
||||
; Player 2 movements
|
||||
.p2_up:
|
||||
mov dx, [p2_y]
|
||||
sub dx, 5
|
||||
mov [p2_y], dx
|
||||
jmp .detect_input
|
||||
ret
|
||||
.p2_down:
|
||||
mov dx, [p2_y]
|
||||
add dx, 5
|
||||
mov [p2_y], dx
|
||||
jmp .detect_input
|
||||
ret
|
||||
|
||||
; Ball bouncing
|
||||
; This should move the ball along one frame
|
||||
@@ -97,17 +97,17 @@ game_pong:
|
||||
|
||||
; Player 1
|
||||
cmp al, 77h ; Pressed 'w' (player 1 up)
|
||||
je .p1_up
|
||||
call .p1_up
|
||||
cmp al, 73h ; Pressed 's' (player 1 down)
|
||||
je .p1_down
|
||||
call .p1_down
|
||||
|
||||
; Player 2
|
||||
cmp al, 5bh ; Pressed '[' (player 2 up)
|
||||
je .p2_up
|
||||
call .p2_up
|
||||
cmp al, 27h ; Pressed ''' (player 2 down)
|
||||
je .p2_down
|
||||
call .p2_down
|
||||
|
||||
call .bounce_ball
|
||||
;call .bounce_ball
|
||||
call .draw_screen
|
||||
|
||||
jmp .detect_input
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
game_snake:
|
||||
call .draw_screen
|
||||
call .detect_input
|
||||
|
||||
.draw_screen:
|
||||
call os_set_graphics_mode ; Clear screen
|
||||
.draw_snake_loop:
|
||||
mov ax, [snake_x]
|
||||
mov [x_start], ax
|
||||
mov [x_end], ax
|
||||
|
||||
mov bx, [snake_y]
|
||||
mov [y_start], bx
|
||||
mov [y_end], bx
|
||||
|
||||
call os_draw_graphical_rectangle
|
||||
ret
|
||||
|
||||
; Player 1 movements
|
||||
.up:
|
||||
mov dword [snake_direction], 0
|
||||
jmp .end_detect_input
|
||||
.down:
|
||||
mov dword [snake_direction], 2
|
||||
jmp .end_detect_input
|
||||
.left:
|
||||
mov dword [snake_direction], 3
|
||||
jmp .end_detect_input
|
||||
.right:
|
||||
mov dword [snake_direction], 1
|
||||
jmp .end_detect_input
|
||||
|
||||
.move_up:
|
||||
mov dx, [snake_y]
|
||||
sub dx, 1
|
||||
mov [snake_y], dx
|
||||
ret
|
||||
.move_down:
|
||||
mov dx, [snake_y]
|
||||
add dx, 1
|
||||
mov [snake_y], dx
|
||||
ret
|
||||
.move_left:
|
||||
mov dx, [snake_x]
|
||||
sub dx, 1
|
||||
mov [snake_x], dx
|
||||
ret
|
||||
.move_right:
|
||||
mov dx, [snake_x]
|
||||
add dx, 1
|
||||
mov [snake_x], dx
|
||||
ret
|
||||
|
||||
.move_snake:
|
||||
cmp dword [snake_direction], 0
|
||||
je .move_up
|
||||
cmp dword [snake_direction], 1
|
||||
je .move_right
|
||||
cmp dword [snake_direction], 2
|
||||
je .move_down
|
||||
; Else it must be left
|
||||
jmp .move_left
|
||||
|
||||
|
||||
.detect_input:
|
||||
call os_read_input
|
||||
cmp al, 08h
|
||||
je .finish
|
||||
|
||||
; Player 1
|
||||
cmp al, 77h ; Pressed 'w' up
|
||||
je .up
|
||||
cmp al, 61h ; Pressed 'a' left
|
||||
je .left
|
||||
cmp al, 73h ; Pressed 's' down
|
||||
je .down
|
||||
cmp al, 64h ; Pressed 'd' right
|
||||
je .right
|
||||
.end_detect_input:
|
||||
call .move_snake
|
||||
call .draw_screen
|
||||
|
||||
jmp .detect_input
|
||||
|
||||
.finish:
|
||||
call os_set_text_mode
|
||||
call os_start_cli
|
||||
|
||||
|
||||
section .data:
|
||||
snake_x: dw 5
|
||||
snake_y: dw 5
|
||||
snake_direction: dw 1 ; 0=up, 1=right, 2=down, 3=left
|
||||
snake_length: dw 1
|
||||
@@ -1,12 +1,19 @@
|
||||
ORG 0h
|
||||
ORG 00h
|
||||
BITS 16
|
||||
root_buffer: equ 24000h ; Stores a dump of the root directory
|
||||
metadata_buffer: equ 27000h ; Stores metadata about whatever's in the data buffer
|
||||
output_buffer: equ 27020h ; Buffer storing stuff you'll output
|
||||
file_buffer: equ 28000h ; Stores actual data to be read
|
||||
|
||||
start:
|
||||
mov si, boot_message
|
||||
call os_print_string_nl
|
||||
call disk_load_root ; Loads the root directory into disk_buffer for future use
|
||||
|
||||
mov si, help_text
|
||||
call os_print_string_nl
|
||||
mov si, boot_message
|
||||
call os_print_string
|
||||
|
||||
mov si, tmp
|
||||
mov ah, 0Eh
|
||||
int 10h
|
||||
|
||||
call os_start_cli
|
||||
hlt
|
||||
@@ -14,19 +21,31 @@ start:
|
||||
halt:
|
||||
jmp halt
|
||||
|
||||
boot_message: db 0Dh, 'Welcome to CrawOS!', 0Dh, 0
|
||||
|
||||
; ------------------------------------------------------------------
|
||||
; FEATURES -- Code to pull into the kernel
|
||||
%INCLUDE "source/kernel/features/text.asm"
|
||||
%INCLUDE "source/kernel/features/keyboard.asm"
|
||||
%INCLUDE "source/kernel/features/cli.asm"
|
||||
%INCLUDE "source/kernel/features/ports.asm"
|
||||
%INCLUDE "source/kernel/features/power.asm"
|
||||
%INCLUDE "source/kernel/features/strings.asm"
|
||||
%INCLUDE "source/kernel/features/graphics.asm"
|
||||
%INCLUDE "source/kernel/features/sound.asm"
|
||||
%INCLUDE "source/kernel/features/basic.asm"
|
||||
%INCLUDE "source/kernel/features/disk.asm"
|
||||
%INCLUDE "source/kernel/features/math.asm"
|
||||
; GAMES -- Games that I wrote for it
|
||||
%INCLUDE "source/kernel/games/pong.asm"
|
||||
%INCLUDE "source/kernel/games/snake.asm"
|
||||
%INCLUDE "source/kernel/features/check.asm"
|
||||
%INCLUDE "source/kernel/features/time.asm"
|
||||
%INCLUDE "source/kernel/features/utils.asm"
|
||||
%INCLUDE "source/kernel/features/cli.asm"
|
||||
%INCLUDE "source/kernel/features/misc.asm"
|
||||
%INCLUDE "source/kernel/features/basic.asm"
|
||||
|
||||
; PROGRAMS
|
||||
%INCLUDE "source/kernel/programs/ed.asm"
|
||||
|
||||
; DATA/VARIABLES
|
||||
%INCLUDE "source/kernel/data.asm"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
279
source/kernel/programs/ed.asm
Normal file
279
source/kernel/programs/ed.asm
Normal file
@@ -0,0 +1,279 @@
|
||||
; Planned commands:
|
||||
; [ ] i insert
|
||||
; [ ] a append
|
||||
; [-] d delete
|
||||
; [x] l prints lines
|
||||
; [ ] w save to disk
|
||||
; [x] q quit
|
||||
ed:
|
||||
pusha
|
||||
call determine_line_count
|
||||
.main_loop:
|
||||
; CLEAR VARIABLES
|
||||
mov [ed_start_int], 0
|
||||
mov [ed_end_int], 0
|
||||
mov [ed_operation_from], 0
|
||||
mov [ed_operation_by], 0
|
||||
|
||||
; Setup prompt
|
||||
mov ax, ed_prompt
|
||||
mov bx, 40
|
||||
call keyboard_display_input ; AX = string location, BX = max prompt length
|
||||
cmp bx, 1 ; I user pressed escape
|
||||
je .quit
|
||||
mov si, ed_prompt
|
||||
mov bx, 0 ; Keeps track of whether comma has passed
|
||||
jmp .get_token
|
||||
|
||||
.error:
|
||||
call ed_error
|
||||
jmp .main_loop
|
||||
|
||||
; Types of tokens:
|
||||
; Number: eg 123
|
||||
; Doller (refers to the last line) $
|
||||
; Comma ,
|
||||
; Command: eg i
|
||||
; eg 123,127i
|
||||
.get_token:
|
||||
lodsb
|
||||
|
||||
; COMMA
|
||||
cmp al, ','
|
||||
je .comma
|
||||
|
||||
; DOLLAR SIGN
|
||||
cmp al, '$'
|
||||
je .dollar
|
||||
|
||||
; NUMBER
|
||||
call check_is_number
|
||||
jc .get_number_token
|
||||
|
||||
; COMMAND
|
||||
call check_is_letter
|
||||
jc .get_letter_token
|
||||
jmp .error
|
||||
|
||||
; Assign a number to either the start or end int (start,end)
|
||||
.dollar:
|
||||
mov ax, [ed_line_count]
|
||||
inc si ; Cancel out the last dec si
|
||||
jmp .after_int_cast
|
||||
.get_number_token:
|
||||
dec si
|
||||
call string_cast_to_int ; AX = integer, DX = length of int string
|
||||
.after_int_cast:
|
||||
cmp bx, 0
|
||||
je .assign_start_int
|
||||
jg .assign_end_int
|
||||
.assign_start_int:
|
||||
mov [ed_start_int], ax
|
||||
jmp .after_int_assign
|
||||
.assign_end_int:
|
||||
mov [ed_end_int], ax
|
||||
|
||||
; Check it's not after the end of the file
|
||||
cmp ax, [ed_line_count]
|
||||
jg .error
|
||||
; Check it's not less than the start int
|
||||
cmp ax, [ed_start_int]
|
||||
jl .error
|
||||
.after_int_assign:
|
||||
dec si
|
||||
jmp .get_token
|
||||
|
||||
|
||||
; Determines a command
|
||||
.get_letter_token:
|
||||
; If ed int
|
||||
cmp [ed_end_int], 0
|
||||
je .set_end_int
|
||||
.after_end_int_check:
|
||||
cmp al, 'l'
|
||||
je .list
|
||||
cmp al, 'd'
|
||||
je .delete
|
||||
cmp al, 'q'
|
||||
je .quit
|
||||
call ed_error
|
||||
jmp .main_loop
|
||||
; If no end int is assigned, set it to the start int
|
||||
.set_end_int:
|
||||
cmp bx,0
|
||||
push ax
|
||||
jne .end_int_to_end ; If , has been passed
|
||||
.end_int_to_start_int:
|
||||
mov ax, [ed_start_int]
|
||||
mov [ed_end_int], ax
|
||||
pop ax
|
||||
jmp .after_end_int_check
|
||||
.end_int_to_end:
|
||||
mov ax, [ed_line_count]
|
||||
mov [ed_end_int], ax
|
||||
pop ax
|
||||
jmp .after_end_int_check
|
||||
|
||||
|
||||
; Changes the value from start int to end int
|
||||
.comma:
|
||||
mov bx, 1
|
||||
jmp .get_token
|
||||
|
||||
|
||||
; COMMANDS
|
||||
; DELETE ;
|
||||
.delete:
|
||||
; Adjust the ed line count
|
||||
mov ax,[ed_end_int]
|
||||
sub ax,[ed_start_int] ; difference in the lines
|
||||
inc ax
|
||||
sub [ed_line_count], ax
|
||||
|
||||
; set up registers to shift data left
|
||||
; Get the byte of where the left shift will start
|
||||
mov bx, [ed_end_int]
|
||||
inc bx
|
||||
call get_byte_of_line ; CX = byte of start of new line (relative to file buffer)
|
||||
mov [ed_operation_from], cx
|
||||
|
||||
; Get the number of bytes to shift left by
|
||||
mov bx, [ed_start_int]
|
||||
call get_byte_of_line
|
||||
mov ax, [ed_operation_from]
|
||||
sub ax, cx
|
||||
mov [ed_operation_by], ax
|
||||
|
||||
; Perform the shift
|
||||
call shift_data_left
|
||||
|
||||
; Adjust the file metadata byte count
|
||||
mov ax, [file_length]
|
||||
sub ax, [ed_operation_by]
|
||||
mov [file_length], ax
|
||||
|
||||
; Exit
|
||||
call text_newline
|
||||
jmp .main_loop
|
||||
|
||||
|
||||
; LIST ;
|
||||
.list:
|
||||
; set up registers to print the data
|
||||
; Get the byte of where the printing will start
|
||||
mov bx, [ed_start_int]
|
||||
call get_byte_of_line ; CX = byte of start of new line (relative to file buffer)
|
||||
mov si, cx
|
||||
|
||||
; Get the number of bytes to print
|
||||
mov bx,[ed_end_int]
|
||||
inc bx
|
||||
call get_byte_of_line
|
||||
sub cx,si
|
||||
|
||||
call text_newline
|
||||
add si, file_buffer
|
||||
call text_print_raw
|
||||
jmp .main_loop
|
||||
|
||||
.quit:
|
||||
popa
|
||||
jmp os_start_cli
|
||||
|
||||
; This could probably go in disk_?
|
||||
shift_data_left:
|
||||
; Calculate source pointer
|
||||
mov si, [ed_operation_by]
|
||||
add si, file_buffer
|
||||
|
||||
; Calculate destination pointer
|
||||
mov di, si
|
||||
sub di, [ed_operation_by]
|
||||
|
||||
; Loop count
|
||||
mov cx, file_length ; STORES THE LENGTH OF THE FILE IN BYTES
|
||||
sub cx, ed_operation_by
|
||||
|
||||
; Perform the actual left shift
|
||||
rep movsb
|
||||
|
||||
; Override the rest with zeroes
|
||||
mov cx, [ed_operation_by]
|
||||
mov al, 0
|
||||
rep stosb
|
||||
|
||||
ret
|
||||
|
||||
ed_error:
|
||||
pusha
|
||||
|
||||
call text_newline
|
||||
mov ah, 0Eh
|
||||
mov al, '?'
|
||||
int 10h
|
||||
call text_newline
|
||||
|
||||
popa
|
||||
ret
|
||||
|
||||
; IN: BX = target line
|
||||
; OUT: CX = relative byte
|
||||
; TODO fix
|
||||
get_byte_of_line:
|
||||
push dx
|
||||
push si
|
||||
push ax
|
||||
|
||||
mov si, file_buffer
|
||||
|
||||
xor dx,dx
|
||||
xor cx,cx
|
||||
|
||||
jmp .increment_line_count
|
||||
.line_loop:
|
||||
xor ax,ax
|
||||
lodsb
|
||||
inc cx
|
||||
|
||||
cmp al, 0Ah
|
||||
je .increment_line_count
|
||||
|
||||
jmp .line_loop
|
||||
.increment_line_count:
|
||||
inc dx
|
||||
cmp dx, bx
|
||||
jl .line_loop
|
||||
jmp .exit
|
||||
.exit:
|
||||
pop ax
|
||||
pop si
|
||||
pop dx
|
||||
ret
|
||||
|
||||
determine_line_count:
|
||||
mov si, file_buffer
|
||||
mov dx, file_length ; STORES THE LENGTH OF THE FILE IN BYTES
|
||||
xor cx,cx
|
||||
.line_count_loop:
|
||||
lodsb
|
||||
cmp al, 0Ah
|
||||
je .increment_line_count
|
||||
dec dx
|
||||
cmp dx, 0
|
||||
jg .line_count_loop
|
||||
mov [ed_line_count], cx
|
||||
ret
|
||||
.increment_line_count:
|
||||
inc cx
|
||||
jmp .line_count_loop
|
||||
|
||||
ed_data_start: db "ED IS COOL"
|
||||
ed_prompt: times 40 db 0
|
||||
ed_start_int: dw 1
|
||||
ed_end_int: dw 0
|
||||
|
||||
ed_line_count: dw 0
|
||||
|
||||
ed_operation_from: dw 0
|
||||
ed_operation_by: dw 0
|
||||
ed_tmp: dd 0
|
||||
@@ -1,6 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script starts the QEMU PC emulator, booting from the
|
||||
# MikeOS floppy disk image
|
||||
|
||||
qemu-system-i386 -soundhw pcspk -drive format=raw,file=disk_images/mikeos.flp,index=0,if=floppy
|
||||
make clean
|
||||
sudo make
|
||||
sudo chown $(whoami) disk_images/*
|
||||
qemu-system-i386\
|
||||
-drive file=disk_images/crawos.img,if=floppy,format=raw\
|
||||
-m 8m\
|
||||
-object memory-backend-file,id=pc.ram,size=8m,mem-path=/dev/shm/qemu-ram,share=on\
|
||||
-machine memory-backend=pc.ram\
|
||||
-d in_asm,int -D ./detailed.log\
|
||||
$1 $2
|
||||
|
||||
Reference in New Issue
Block a user