Compare commits
11 Commits
7eea8d0ce1
...
ed
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
245cfa57f3 | ||
|
|
88716c0b64 | ||
|
|
7a9b82d57c | ||
|
|
43d0a020f7 | ||
| beca477ff9 | |||
|
|
02657c3fcd | ||
|
|
a3ed2c801a | ||
| 696f8198ad | |||
| 04ab45dd36 | |||
|
|
4a655e991e | ||
|
|
54d0665456 |
7
Makefile
7
Makefile
@@ -15,7 +15,7 @@ $(BUILD_DIR)/crawos.iso: floppy_image
|
|||||||
# Floppy image
|
# Floppy image
|
||||||
# Fat12
|
# Fat12
|
||||||
floppy_image: $(BUILD_DIR)/crawos.img
|
floppy_image: $(BUILD_DIR)/crawos.img
|
||||||
$(BUILD_DIR)/crawos.img: bootloader kernel
|
$(BUILD_DIR)/crawos.img: bootloader kernel check-fat83
|
||||||
mkdir -p disk_images
|
mkdir -p disk_images
|
||||||
dd if=/dev/zero of=$(BUILD_DIR)/crawos.img bs=512 count=2880 # Use dd to make a disk image
|
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
|
mkfs.fat -F 12 -n "CRAWOS" $(BUILD_DIR)/crawos.img # Format the disk image with fat12
|
||||||
@@ -36,6 +36,11 @@ $(BUILD_DIR)/kernel.bin:
|
|||||||
mkdir -p disk_images
|
mkdir -p disk_images
|
||||||
$(ASM) $(SRC_DIR)/kernel/kernel.asm -f bin -o $ $(BUILD_DIR)/kernel.bin
|
$(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
|
||||||
clean:
|
clean:
|
||||||
rm -f disk_images/*
|
rm -f disk_images/*
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -35,6 +35,9 @@ 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/>
|
Or it can be burnt to a physical disk to be booted from<br/>
|
||||||
hardware.
|
hardware.
|
||||||
|
> [!NOTE]
|
||||||
|
> The VM setup has only been tested on OpenSUSE and NixOS Linux,<br/>
|
||||||
|
> please report any bugs.
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
In order to run this project in testing mode you can use<br/>
|
In order to run this project in testing mode you can use<br/>
|
||||||
@@ -49,6 +52,15 @@ To allow for easier debugging.
|
|||||||
# BASIC development
|
# BASIC development
|
||||||
Please refer to the [MikeOS BASIC documentation](https://mikeos.sourceforge.net/handbook-appdev-basic.html)<br/>
|
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/>
|
docs on writing BASIC code. In order to create new<br/>
|
||||||
files, place them in the /data/ directory, not the file<br/>
|
files, place them in the /data/ directory, note the file<br/>
|
||||||
names are limited to 11 characters due to FAT12 limitations<br/>
|
names are limited to 11 characters due to FAT12 limitations<br/>
|
||||||
(8 for the name and 3 for the extension).
|
(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.
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
Kernel written in Assmebly
|
# What is this?
|
||||||
I'm using a modified JazzOS Bootloader
|
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:
|
# Commands
|
||||||
CAT
|
- CAT <filename>: outputs the contents of the file.
|
||||||
HELP
|
- LS: outputs a list of files on the system .
|
||||||
CLEAR
|
- CLEAR: clears the screen.
|
||||||
REBOOT
|
- REBOOT: or esc reboots the system
|
||||||
PONG
|
- BAS <filename>: runs a basic script.
|
||||||
|
|||||||
16
data/fb.bas
16
data/fb.bas
@@ -1,9 +1,11 @@
|
|||||||
FOR A = 1 TO 100
|
# A cool fizzbuzz program
|
||||||
PRINT A ;
|
PRINT "FizzBuzz!"
|
||||||
B = A % 3
|
FOR A = 1 TO 16
|
||||||
C = A % 5
|
PRINT A ; # Prints the number
|
||||||
IF B = 0 then PRINT "Fizz" ;
|
B = A % 3
|
||||||
IF C = 0 then PRINT "Buzz" ;
|
C = A % 5
|
||||||
PRINT ""
|
IF B = 0 then PRINT "Fizz" ;
|
||||||
|
IF C = 0 then PRINT "Buzz" ;
|
||||||
|
PRINT ""
|
||||||
NEXT A
|
NEXT A
|
||||||
END
|
END
|
||||||
|
|||||||
@@ -1,17 +1,5 @@
|
|||||||
$1 = "_________"
|
PRINT "Hello, what is your name?"
|
||||||
A = & $1
|
INPUT $1
|
||||||
FOR X = 1 TO 9
|
PRINT "Hello " ;
|
||||||
FOR Y = 1 TO 9
|
PRINT $1
|
||||||
PEEK B A
|
|
||||||
IF B = 95 THEN PRINT "_" ;
|
|
||||||
IF B = 88 THEN PRINT "X" ;
|
|
||||||
IF B = 79 THEN PRINT "O" ;
|
|
||||||
C = A % 3
|
|
||||||
IF C = 0 THEN PRINT " "
|
|
||||||
A = A + 1
|
|
||||||
NEXT Y
|
|
||||||
Z = X % 2
|
|
||||||
IF Z = 0 THEN PRINT "You are X"
|
|
||||||
IF Z = 1 THEN PRINT "You are O"
|
|
||||||
NEXT X
|
|
||||||
END
|
END
|
||||||
|
|||||||
2
data/test.asm
Normal file
2
data/test.asm
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
X = 100
|
||||||
|
PRINT X
|
||||||
@@ -1 +0,0 @@
|
|||||||
x = 5
|
|
||||||
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/>
|
||||||
@@ -26,7 +26,7 @@ ebr_system_id: db 'FAT12 ' ; must be 8 bytes
|
|||||||
fat12_file_name: db ' ' ; 11 free bytes to store a filename in
|
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
|
boot_message: db 'OK] Kernel successfully loaded!\n"HELP" to see a list of available commands\n', 0
|
||||||
user_input: times 20 db 0
|
user_input: times 20 db 0
|
||||||
prompt_length: db 20
|
prompt_length: dw 20
|
||||||
prompt: db 'sh > ', 0
|
prompt: db 'sh > ', 0
|
||||||
help_string: db 'HELP', 0
|
help_string: db 'HELP', 0
|
||||||
clear_string: db 'CLEAR', 0
|
clear_string: db 'CLEAR', 0
|
||||||
@@ -34,7 +34,8 @@ reboot_string: db 'REBOOT', 0
|
|||||||
basic_string: db 'BAS', 0
|
basic_string: db 'BAS', 0
|
||||||
cat_string: db 'CAT', 0
|
cat_string: db 'CAT', 0
|
||||||
ls_string: db 'LS', 0
|
ls_string: db 'LS', 0
|
||||||
help_text: db 'This is for Cowards:\n"LS" to list the directory,\n"CAT" to output the contents of a file,\n"BAS" 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
|
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
|
basic_text: db 'BASIC PROGRAM BEGUN:\n', 0
|
||||||
command_result_text: db 'You typed: ', 0
|
command_result_text: db 'You typed: ', 0
|
||||||
unknown_command: db 'Error: Unkown Command..\n ', 0
|
unknown_command: db 'Error: Unkown Command..\n ', 0
|
||||||
@@ -47,12 +48,6 @@ file_not_found: db 'File not found\n', 0
|
|||||||
too_long_filename: db 'Filename too long for Fat12\n', 0
|
too_long_filename: db 'Filename too long for Fat12\n', 0
|
||||||
file_found: db 'File found\n', 0
|
file_found: db 'File found\n', 0
|
||||||
loading_root: db 'Loading root diretory\n', 0
|
loading_root: db 'Loading root diretory\n', 0
|
||||||
read_only: db 'Read Only', 0 ; 1
|
|
||||||
hidden: db ' Hidden ', 0 ; 2
|
|
||||||
system: db ' System ', 0 ; 4
|
|
||||||
volume: db ' Volume ', 0 ; 8
|
|
||||||
directory: db 'Directory', 0 ; 10
|
|
||||||
archive: db ' Archive ', 0 ; 20
|
|
||||||
file_name_length: db 0
|
file_name_length: db 0
|
||||||
file_cluster: dw 0
|
file_cluster: dw 0
|
||||||
file_length: dd 0
|
file_length: dd 0
|
||||||
|
|||||||
@@ -842,14 +842,14 @@ do_break:
|
|||||||
do_call:
|
do_call:
|
||||||
call get_token
|
call get_token
|
||||||
cmp ax, NUMBER
|
cmp ax, NUMBER
|
||||||
je .is_number
|
je .check_is_number
|
||||||
|
|
||||||
mov ax, 0
|
mov ax, 0
|
||||||
mov byte al, [token]
|
mov byte al, [token]
|
||||||
call get_var
|
call get_var
|
||||||
jmp .execute_call
|
jmp .execute_call
|
||||||
|
|
||||||
.is_number:
|
.check_is_number:
|
||||||
mov si, token
|
mov si, token
|
||||||
call string_cast_to_int
|
call string_cast_to_int
|
||||||
|
|
||||||
@@ -1325,7 +1325,7 @@ do_for:
|
|||||||
cmp ax, NUMBER
|
cmp ax, NUMBER
|
||||||
jne .error
|
jne .error
|
||||||
|
|
||||||
.second_is_number:
|
.second_check_is_number:
|
||||||
mov si, token ; Get target number
|
mov si, token ; Get target number
|
||||||
call string_cast_to_int
|
call string_cast_to_int
|
||||||
jmp .continue2
|
jmp .continue2
|
||||||
@@ -1625,7 +1625,7 @@ do_if:
|
|||||||
je .equals_char
|
je .equals_char
|
||||||
|
|
||||||
mov byte al, [token]
|
mov byte al, [token]
|
||||||
call is_letter
|
call check_is_letter
|
||||||
jc .equals_var
|
jc .equals_var
|
||||||
|
|
||||||
mov si, token ; Otherwise it's, eg 'X = 1' (a number)
|
mov si, token ; Otherwise it's, eg 'X = 1' (a number)
|
||||||
@@ -1711,7 +1711,7 @@ do_if:
|
|||||||
.greater:
|
.greater:
|
||||||
call get_token ; Greater than a variable or number?
|
call get_token ; Greater than a variable or number?
|
||||||
mov byte al, [token]
|
mov byte al, [token]
|
||||||
call is_letter
|
call check_is_letter
|
||||||
jc .greater_var
|
jc .greater_var
|
||||||
|
|
||||||
mov si, token ; Must be a number here...
|
mov si, token ; Must be a number here...
|
||||||
@@ -1736,7 +1736,7 @@ do_if:
|
|||||||
.less:
|
.less:
|
||||||
call get_token
|
call get_token
|
||||||
mov byte al, [token]
|
mov byte al, [token]
|
||||||
call is_letter
|
call check_is_letter
|
||||||
jc .less_var
|
jc .less_var
|
||||||
|
|
||||||
mov si, token
|
mov si, token
|
||||||
@@ -1950,8 +1950,9 @@ do_input:
|
|||||||
|
|
||||||
.number_var:
|
.number_var:
|
||||||
mov ax, .tmpstring ; Get input from the user
|
mov ax, .tmpstring ; Get input from the user
|
||||||
mov bx, 6
|
mov bx, 20
|
||||||
call keyboard_display_input
|
call keyboard_display_input
|
||||||
|
jc .end
|
||||||
|
|
||||||
mov ax, .tmpstring
|
mov ax, .tmpstring
|
||||||
call string_length
|
call string_length
|
||||||
@@ -1973,8 +1974,6 @@ do_input:
|
|||||||
call os_print_newline
|
call os_print_newline
|
||||||
|
|
||||||
jmp mainloop
|
jmp mainloop
|
||||||
|
|
||||||
|
|
||||||
.string_var:
|
.string_var:
|
||||||
mov ax, 128
|
mov ax, 128
|
||||||
mul bx
|
mul bx
|
||||||
@@ -1987,8 +1986,9 @@ do_input:
|
|||||||
|
|
||||||
jmp mainloop
|
jmp mainloop
|
||||||
|
|
||||||
|
.tmpstring times 20 db 0
|
||||||
.tmpstring times 6 db 0
|
.end
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
; -----------------------------------------------------------
|
; -----------------------------------------------------------
|
||||||
@@ -2636,7 +2636,7 @@ do_peekint:
|
|||||||
cmp ax, NUMBER
|
cmp ax, NUMBER
|
||||||
jne .error
|
jne .error
|
||||||
|
|
||||||
.address_is_number:
|
.address_check_is_number:
|
||||||
mov si, token
|
mov si, token
|
||||||
call string_cast_to_int
|
call string_cast_to_int
|
||||||
jmp .load_data
|
jmp .load_data
|
||||||
@@ -3770,14 +3770,14 @@ do_string:
|
|||||||
call get_token ; Now there should be a number
|
call get_token ; Now there should be a number
|
||||||
|
|
||||||
cmp ax, NUMBER
|
cmp ax, NUMBER
|
||||||
je .third_is_number
|
je .third_check_is_number
|
||||||
|
|
||||||
cmp ax, VARIABLE
|
cmp ax, VARIABLE
|
||||||
je .third_is_variable
|
je .third_is_variable
|
||||||
|
|
||||||
jmp .error
|
jmp .error
|
||||||
|
|
||||||
.third_is_number:
|
.third_check_is_number:
|
||||||
mov si, token
|
mov si, token
|
||||||
call string_cast_to_int
|
call string_cast_to_int
|
||||||
jmp .got_number
|
jmp .got_number
|
||||||
@@ -3970,13 +3970,21 @@ get_token:
|
|||||||
mov word si, [prog]
|
mov word si, [prog]
|
||||||
lodsb
|
lodsb
|
||||||
|
|
||||||
|
cmp al, 09h
|
||||||
|
je .whitespace
|
||||||
|
cmp al, 20h
|
||||||
|
je .whitespace
|
||||||
|
|
||||||
|
cmp al, '#'
|
||||||
|
je .comment
|
||||||
|
|
||||||
cmp al, 10
|
cmp al, 10
|
||||||
je .newline
|
je .newline
|
||||||
|
|
||||||
cmp al, ' '
|
cmp al, ' '
|
||||||
je .newline
|
je .newline
|
||||||
|
|
||||||
call is_number
|
call check_is_number
|
||||||
jc get_number_token
|
jc get_number_token
|
||||||
|
|
||||||
cmp al, '"'
|
cmp al, '"'
|
||||||
@@ -3990,13 +3998,28 @@ get_token:
|
|||||||
|
|
||||||
jmp get_string_token
|
jmp get_string_token
|
||||||
|
|
||||||
|
.whitespace:
|
||||||
|
inc word [prog]
|
||||||
|
jmp get_token
|
||||||
|
|
||||||
|
.comment:
|
||||||
|
inc word [prog] ; Move past first quote (") char
|
||||||
|
mov word si, [prog]
|
||||||
|
mov di, token
|
||||||
|
.loop:
|
||||||
|
lodsb
|
||||||
|
cmp al, 0Ah
|
||||||
|
je .done
|
||||||
|
inc word [prog]
|
||||||
|
jmp .loop
|
||||||
|
.done:
|
||||||
|
inc word [prog] ; Move past final quote
|
||||||
|
jmp get_token
|
||||||
|
|
||||||
.newline:
|
.newline:
|
||||||
inc word [prog]
|
inc word [prog]
|
||||||
jmp get_token
|
jmp get_token
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
get_number_token:
|
get_number_token:
|
||||||
mov word si, [prog]
|
mov word si, [prog]
|
||||||
mov di, token
|
mov di, token
|
||||||
@@ -4007,7 +4030,7 @@ get_number_token:
|
|||||||
je .done
|
je .done
|
||||||
cmp al, ' '
|
cmp al, ' '
|
||||||
je .done
|
je .done
|
||||||
call is_number
|
call check_is_number
|
||||||
jc .fine
|
jc .fine
|
||||||
|
|
||||||
mov si, err_char_in_num
|
mov si, err_char_in_num
|
||||||
@@ -4130,7 +4153,7 @@ get_string_token:
|
|||||||
|
|
||||||
.is_not_string:
|
.is_not_string:
|
||||||
mov byte al, [token]
|
mov byte al, [token]
|
||||||
call is_letter
|
call check_is_letter
|
||||||
jc .is_var
|
jc .is_var
|
||||||
|
|
||||||
mov ax, UNKNOWN
|
mov ax, UNKNOWN
|
||||||
@@ -4141,37 +4164,6 @@ get_string_token:
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
; ------------------------------------------------------------------
|
|
||||||
; Set carry flag if AL contains ASCII number
|
|
||||||
|
|
||||||
is_number:
|
|
||||||
cmp al, 48
|
|
||||||
jl .not_number
|
|
||||||
cmp al, 57
|
|
||||||
jg .not_number
|
|
||||||
stc
|
|
||||||
ret
|
|
||||||
.not_number:
|
|
||||||
clc
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
; ------------------------------------------------------------------
|
|
||||||
; Set carry flag if AL contains ASCII letter
|
|
||||||
|
|
||||||
is_letter:
|
|
||||||
cmp al, 65
|
|
||||||
jl .not_letter
|
|
||||||
cmp al, 90
|
|
||||||
jg .not_letter
|
|
||||||
stc
|
|
||||||
ret
|
|
||||||
|
|
||||||
.not_letter:
|
|
||||||
clc
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
; ------------------------------------------------------------------
|
; ------------------------------------------------------------------
|
||||||
; Print error message and quit out
|
; Print error message and quit out
|
||||||
|
|
||||||
@@ -4273,6 +4265,8 @@ vars_loc:
|
|||||||
work_page db 0 ; Page to print to
|
work_page db 0 ; Page to print to
|
||||||
disp_page db 0 ; Page to display
|
disp_page db 0 ; Page to display
|
||||||
|
|
||||||
|
tab_skip db " ", 0
|
||||||
|
space_skip db " ", 0
|
||||||
alert_cmd db "ALERT", 0
|
alert_cmd db "ALERT", 0
|
||||||
askfile_cmd db "ASKFILE", 0
|
askfile_cmd db "ASKFILE", 0
|
||||||
break_cmd db "BREAK", 0
|
break_cmd db "BREAK", 0
|
||||||
|
|||||||
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
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
os_start_cli:
|
os_start_cli:
|
||||||
pusha
|
pusha
|
||||||
|
|
||||||
|
|
||||||
call os_print_newline
|
call os_print_newline
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ os_start_cli:
|
|||||||
mov ax, user_input
|
mov ax, user_input
|
||||||
|
|
||||||
call keyboard_display_input
|
call keyboard_display_input
|
||||||
|
jc power_reboot
|
||||||
jmp os_read_cli
|
jmp os_read_cli
|
||||||
|
|
||||||
|
|
||||||
@@ -30,14 +32,14 @@ os_read_cli:
|
|||||||
mov di, help_string
|
mov di, help_string
|
||||||
call os_compare_strings
|
call os_compare_strings
|
||||||
cmp cl, 1
|
cmp cl, 1
|
||||||
je help
|
je .help
|
||||||
|
|
||||||
; Clear screen
|
; Clear screen
|
||||||
mov si, user_input
|
mov si, user_input
|
||||||
mov di, clear_string
|
mov di, clear_string
|
||||||
call os_compare_strings
|
call os_compare_strings
|
||||||
cmp cl, 1
|
cmp cl, 1
|
||||||
je clear
|
je .clear
|
||||||
|
|
||||||
; Reboot
|
; Reboot
|
||||||
mov si, user_input
|
mov si, user_input
|
||||||
@@ -51,22 +53,29 @@ os_read_cli:
|
|||||||
mov di, basic_string
|
mov di, basic_string
|
||||||
call os_compare_strings
|
call os_compare_strings
|
||||||
cmp cl, 1
|
cmp cl, 1
|
||||||
je basic
|
je .basic
|
||||||
|
|
||||||
; Cat
|
; Cat
|
||||||
mov si, user_input
|
mov si, user_input
|
||||||
mov di, cat_string
|
mov di, cat_string
|
||||||
call os_compare_strings
|
call os_compare_strings
|
||||||
cmp cl, 1
|
cmp cl, 1
|
||||||
je cat
|
je .cat
|
||||||
|
|
||||||
; LS
|
; LS
|
||||||
mov si, user_input
|
mov si, user_input
|
||||||
mov di, ls_string
|
mov di, ls_string
|
||||||
call os_compare_strings
|
call os_compare_strings
|
||||||
cmp cl, 1
|
cmp cl, 1
|
||||||
je ls
|
je .ls
|
||||||
|
|
||||||
|
;ED
|
||||||
|
mov si, user_input
|
||||||
|
mov di, ed_string
|
||||||
|
call os_compare_strings
|
||||||
|
cmp cl, 1
|
||||||
|
je .ed
|
||||||
|
|
||||||
jmp .unkown
|
jmp .unkown
|
||||||
|
|
||||||
.unkown:
|
.unkown:
|
||||||
@@ -77,24 +86,33 @@ os_read_cli:
|
|||||||
jmp .finish
|
jmp .finish
|
||||||
|
|
||||||
.finish:
|
.finish:
|
||||||
|
; Clear the user input
|
||||||
|
mov al, 0
|
||||||
|
mov cx, 20
|
||||||
|
mov di,user_input
|
||||||
|
repe stosb
|
||||||
popa
|
popa
|
||||||
call os_start_cli
|
call os_start_cli
|
||||||
|
|
||||||
clear:
|
.clear:
|
||||||
call os_set_text_mode
|
call os_set_text_mode
|
||||||
call os_read_cli.finish
|
call os_read_cli.finish
|
||||||
|
|
||||||
help:
|
.help:
|
||||||
mov si, help_text
|
mov si, help_text
|
||||||
call os_print_string
|
call os_print_string
|
||||||
call os_read_cli.finish
|
call os_read_cli.finish
|
||||||
|
|
||||||
basic:
|
.basic:
|
||||||
call util_basic
|
call util_basic
|
||||||
call os_read_cli.finish
|
call os_read_cli.finish
|
||||||
cat:
|
.cat:
|
||||||
call util_cat
|
call util_cat
|
||||||
call os_read_cli.finish
|
call os_read_cli.finish
|
||||||
ls:
|
.ls:
|
||||||
call util_ls
|
call util_ls
|
||||||
call os_read_cli.finish
|
call os_read_cli.finish
|
||||||
|
.ed:
|
||||||
|
call util_ed
|
||||||
|
call os_read_cli.finish
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ disk_clear_output_buffer:
|
|||||||
; OUT
|
; OUT
|
||||||
; data_buffer: file contents
|
; data_buffer: file contents
|
||||||
; TODO use predefined data for calculations
|
; TODO use predefined data for calculations
|
||||||
|
; CX = 0 if failed to load
|
||||||
disk_load_file:
|
disk_load_file:
|
||||||
pusha
|
pusha
|
||||||
call string_length ; cl = string length
|
call string_length ; cl = string length
|
||||||
@@ -227,13 +228,18 @@ disk_load_file:
|
|||||||
.file_not_found:
|
.file_not_found:
|
||||||
mov si, file_not_found
|
mov si, file_not_found
|
||||||
call os_print_string
|
call os_print_string
|
||||||
jmp .done
|
jmp .done_fail
|
||||||
.filename_too_long:
|
.filename_too_long:
|
||||||
mov si, too_long_filename
|
mov si, too_long_filename
|
||||||
call os_print_string
|
call os_print_string
|
||||||
jmp .done
|
jmp .done_fail
|
||||||
|
.done_fail:
|
||||||
|
popa
|
||||||
|
mov bx, 1
|
||||||
|
ret
|
||||||
.done:
|
.done:
|
||||||
popa
|
popa
|
||||||
|
xor bx,bx
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; Write data to file
|
; Write data to file
|
||||||
@@ -354,12 +360,12 @@ disk_list_contents:
|
|||||||
popa
|
popa
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
disk_file_list:
|
||||||
|
ret
|
||||||
disk_file_exists:
|
disk_file_exists:
|
||||||
ret
|
ret
|
||||||
disk_file_size:
|
disk_file_size:
|
||||||
ret
|
ret
|
||||||
disk_file_list:
|
|
||||||
ret
|
|
||||||
disk_remove_file:
|
disk_remove_file:
|
||||||
ret
|
ret
|
||||||
disk_rename_file:
|
disk_rename_file:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
; AX = key pressed
|
; AX = key pressed
|
||||||
|
; Waits until key is pressed before returning
|
||||||
os_read_input:
|
os_read_input:
|
||||||
mov ah, 11h ; BIOS call to check for key
|
mov ah, 11h ; BIOS call to check for key
|
||||||
int 16h ; Interrupt
|
int 16h ; Interrupt
|
||||||
@@ -12,21 +13,28 @@ os_read_input:
|
|||||||
int 16h
|
int 16h
|
||||||
ret
|
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
|
||||||
; -------------------------------------------
|
; -------------------------------------------
|
||||||
; IN:
|
; IN:
|
||||||
; AX = output address
|
; AX = output address
|
||||||
; BX = max length
|
; BX = max length
|
||||||
|
; OUT:
|
||||||
|
; BX = 1 if escape is pressed
|
||||||
keyboard_display_input:
|
keyboard_display_input:
|
||||||
pusha
|
pusha
|
||||||
mov di, ax
|
mov di, ax
|
||||||
mov ax, bx ; Lazy TODO
|
xor ax,ax
|
||||||
mov cx, [prompt_length]
|
mov [prompt_length], bx
|
||||||
|
|
||||||
.loop:
|
|
||||||
jmp .check_key_pressed
|
|
||||||
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.check_key_pressed:
|
.check_key_pressed:
|
||||||
call os_read_input
|
call os_read_input
|
||||||
@@ -40,25 +48,30 @@ keyboard_display_input:
|
|||||||
cmp al, 1Bh
|
cmp al, 1Bh
|
||||||
je .esc_key
|
je .esc_key
|
||||||
|
|
||||||
cmp cx, 0
|
dec bx
|
||||||
jb .check_key_pressed
|
|
||||||
|
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
|
jmp .check_key_pressed
|
||||||
|
|
||||||
.esc_key:
|
.esc_key:
|
||||||
call power_reboot
|
popa
|
||||||
|
mov bx, 1
|
||||||
|
ret
|
||||||
|
|
||||||
.enter_key:
|
.enter_key:
|
||||||
mov al, 0
|
mov al, 0
|
||||||
stosb
|
stosb
|
||||||
popa
|
popa
|
||||||
|
xor bx,bx
|
||||||
ret ; Return to the parent function (whatever that may be)
|
ret ; Return to the parent function (whatever that may be)
|
||||||
|
|
||||||
.backspace:
|
.backspace:
|
||||||
jmp .move_cursor_back ; then .move_cursor_back
|
cmp bx, [prompt_length] ; Cannot backspace if the cursor is at the start
|
||||||
jmp .loop ; Else .loop
|
jb .move_cursor_back ; then .move_cursor_back
|
||||||
|
jmp .check_key_pressed ; Else check the next key
|
||||||
|
|
||||||
.move_cursor_back:
|
.move_cursor_back:
|
||||||
mov ah, 0Eh
|
mov ah, 0Eh
|
||||||
@@ -70,20 +83,20 @@ keyboard_display_input:
|
|||||||
mov al, 08h
|
mov al, 08h
|
||||||
int 10h
|
int 10h
|
||||||
|
|
||||||
|
inc bx
|
||||||
dec di
|
dec di
|
||||||
jmp .loop
|
jmp .check_key_pressed
|
||||||
|
|
||||||
.print_current_input: ; Echo back that character and return the key inputing
|
.print_current_input: ; Echo back that character and return the key inputing
|
||||||
stosb
|
stosb
|
||||||
|
|
||||||
mov ah, 0Eh
|
mov ah, 0Eh
|
||||||
int 10h
|
int 10h
|
||||||
|
jmp .check_key_pressed
|
||||||
|
.input_length db 0
|
||||||
|
|
||||||
ret
|
keyboard_get_cursor_pos:
|
||||||
|
|
||||||
keyboard_get_cursor_pos: ;TODO
|
|
||||||
ret
|
ret
|
||||||
keyboard_wait_for_key: ;TODO
|
keyboard_wait_for_key:
|
||||||
ret
|
ret
|
||||||
keyboard_show_cursor:
|
keyboard_show_cursor:
|
||||||
ret
|
ret
|
||||||
@@ -91,5 +104,3 @@ keyboard_hide_cursor:
|
|||||||
ret
|
ret
|
||||||
keyboard_move_cursor:
|
keyboard_move_cursor:
|
||||||
ret
|
ret
|
||||||
keyboard_check_key:
|
|
||||||
ret
|
|
||||||
|
|||||||
@@ -1,7 +1,20 @@
|
|||||||
|
; IN:
|
||||||
|
; DX = port address
|
||||||
|
; AL = byte
|
||||||
port_byte_out:
|
port_byte_out:
|
||||||
|
pusha
|
||||||
|
out dx,al
|
||||||
|
popa
|
||||||
ret
|
ret
|
||||||
|
; -----------------------------------------
|
||||||
|
; IN:
|
||||||
|
; DX = port address
|
||||||
|
; OUT:
|
||||||
|
; AL = byte from port
|
||||||
port_byte_in:
|
port_byte_in:
|
||||||
|
in al, dx
|
||||||
ret
|
ret
|
||||||
|
|
||||||
port_serial_enable:
|
port_serial_enable:
|
||||||
ret
|
ret
|
||||||
port_send_via_serial:
|
port_send_via_serial:
|
||||||
|
|||||||
@@ -106,35 +106,42 @@ string_join:
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
; ------------------------------------------------------------------------
|
; ------------------------------------------------------------------------
|
||||||
; TODO
|
; Converts a string to an integer
|
||||||
; Converts a null terminated string to an integer
|
|
||||||
; IN: SI = string location (max 5 chars, up to '65536')
|
; IN: SI = string location (max 5 chars, up to '65536')
|
||||||
; OUT: AX = number
|
; OUT: AX = number, DX = length of int string
|
||||||
string_cast_to_int:
|
string_cast_to_int:
|
||||||
pusha
|
push cx
|
||||||
|
push bx
|
||||||
|
push dx
|
||||||
xor cx,cx
|
xor cx,cx
|
||||||
.loop:
|
.loop:
|
||||||
xor ax,ax
|
xor ax,ax
|
||||||
lodsb
|
lodsb
|
||||||
cmp al, 0
|
|
||||||
je .finish
|
; Exit if the character is not a number
|
||||||
sub al, 30h
|
cmp al,'0'
|
||||||
|
jl .finish
|
||||||
|
cmp al,'9'
|
||||||
|
jg .finish
|
||||||
|
|
||||||
|
sub al, '0'
|
||||||
mov bx, ax
|
mov bx, ax
|
||||||
|
|
||||||
mov ax, 10
|
mov ax, 10
|
||||||
mul cx ; Multiple the current value by 10
|
mul cx ; Multiple the current value by 10
|
||||||
add ax, bx ; Then add the new digit
|
add ax, bx ; Then add the new digit
|
||||||
mov cx, ax
|
mov cx, ax
|
||||||
jmp .loop
|
jmp .loop
|
||||||
.finish:
|
.finish:
|
||||||
mov [int_tmp], cx
|
mov ax,cx
|
||||||
popa
|
pop dx
|
||||||
mov ax, [int_tmp]
|
pop bx
|
||||||
|
pop cx
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; ------------------------------------------------------------------------
|
; ------------------------------------------------------------------------
|
||||||
; IN: AX = integer (unsigned)
|
; IN: AX = integer (unsigned)
|
||||||
; OUT: AX -> null-terminated string
|
; OUT: AX -> null-terminated string
|
||||||
|
|
||||||
string_cast_from_int:
|
string_cast_from_int:
|
||||||
pusha
|
pusha
|
||||||
cld ; Write backwards
|
cld ; Write backwards
|
||||||
@@ -331,7 +338,5 @@ string_lower_case: ; to lower case
|
|||||||
popa
|
popa
|
||||||
ret
|
ret
|
||||||
|
|
||||||
string_input:
|
|
||||||
ret
|
|
||||||
string_print_2hex:
|
string_print_2hex:
|
||||||
ret
|
ret
|
||||||
|
|||||||
@@ -55,10 +55,16 @@ text_print_raw:
|
|||||||
je .space
|
je .space
|
||||||
cmp al, 0Ah ; When there's an NL or CR, do both, linux encodes files only with NL
|
cmp al, 0Ah ; When there's an NL or CR, do both, linux encodes files only with NL
|
||||||
je .new_line
|
je .new_line
|
||||||
|
cmp al, 09h ; TAB
|
||||||
|
je .tab
|
||||||
cmp al, 0Dh
|
cmp al, 0Dh
|
||||||
je .repeat
|
je .repeat
|
||||||
int 10h
|
int 10h
|
||||||
jmp .repeat
|
jmp .repeat
|
||||||
|
.tab:
|
||||||
|
mov al, ' '
|
||||||
|
int 10h
|
||||||
|
jmp .repeat
|
||||||
.space:
|
.space:
|
||||||
mov al, 20h
|
mov al, 20h
|
||||||
int 10h
|
int 10h
|
||||||
@@ -129,6 +135,15 @@ os_set_text_mode:
|
|||||||
mov dh, 0
|
mov dh, 0
|
||||||
mov dl, 0
|
mov dl, 0
|
||||||
int 10h
|
int 10h
|
||||||
|
|
||||||
|
text_newline:
|
||||||
|
pusha
|
||||||
|
mov ax,0E0Ah
|
||||||
|
int 10h
|
||||||
|
mov al,0Dh
|
||||||
|
int 10h
|
||||||
|
popa
|
||||||
|
ret
|
||||||
|
|
||||||
popa
|
popa
|
||||||
ret
|
ret
|
||||||
|
|||||||
@@ -1,30 +1,33 @@
|
|||||||
util_cat:
|
util_cat:
|
||||||
pusha
|
pusha
|
||||||
|
|
||||||
call disk_clear_file_buffer
|
call disk_clear_file_buffer
|
||||||
mov si, user_input
|
mov si, user_input
|
||||||
; TODO make this more consistent to account for double spaces
|
; TODO make this more consistent to account for double spaces
|
||||||
add si, 4 ; Move si to after 'CAT'
|
add si, 4 ; Move si to after 'CAT'
|
||||||
|
|
||||||
call disk_load_file
|
call disk_load_file
|
||||||
mov si, file_buffer
|
cmp bx, 1
|
||||||
mov cx, [file_length]
|
je .done
|
||||||
call text_print_raw
|
|
||||||
|
|
||||||
popa
|
mov si, file_buffer
|
||||||
ret
|
mov cx, [file_length]
|
||||||
|
call text_print_raw
|
||||||
|
.done
|
||||||
|
popa
|
||||||
|
ret
|
||||||
|
|
||||||
util_ls:
|
util_ls:
|
||||||
pusha
|
pusha
|
||||||
|
|
||||||
call disk_list_contents
|
call disk_list_contents
|
||||||
|
|
||||||
mov si, output_buffer
|
mov si, output_buffer
|
||||||
call os_print_string
|
call os_print_string
|
||||||
call disk_clear_output_buffer
|
call disk_clear_output_buffer
|
||||||
|
|
||||||
popa
|
popa
|
||||||
ret
|
ret
|
||||||
|
|
||||||
util_basic:
|
util_basic:
|
||||||
pusha
|
pusha
|
||||||
@@ -34,6 +37,8 @@ util_basic:
|
|||||||
; TODO make this more consistent to account for double spaces
|
; TODO make this more consistent to account for double spaces
|
||||||
;add si, 3 ; Move si to after 'BAS'
|
;add si, 3 ; Move si to after 'BAS'
|
||||||
call disk_load_file
|
call disk_load_file
|
||||||
|
cmp bx, 1
|
||||||
|
je .done
|
||||||
|
|
||||||
mov si, file_buffer
|
mov si, file_buffer
|
||||||
|
|
||||||
@@ -41,5 +46,16 @@ util_basic:
|
|||||||
mov si, 0
|
mov si, 0
|
||||||
call basic_run_basic
|
call basic_run_basic
|
||||||
|
|
||||||
|
.done
|
||||||
|
popa
|
||||||
|
ret
|
||||||
|
|
||||||
|
util_ed:
|
||||||
|
pusha
|
||||||
|
call disk_load_root
|
||||||
|
call disk_load_file
|
||||||
|
|
||||||
|
call ed
|
||||||
|
|
||||||
popa
|
popa
|
||||||
ret
|
ret
|
||||||
|
|||||||
@@ -33,13 +33,19 @@ halt:
|
|||||||
%INCLUDE "source/kernel/features/sound.asm"
|
%INCLUDE "source/kernel/features/sound.asm"
|
||||||
%INCLUDE "source/kernel/features/disk.asm"
|
%INCLUDE "source/kernel/features/disk.asm"
|
||||||
%INCLUDE "source/kernel/features/math.asm"
|
%INCLUDE "source/kernel/features/math.asm"
|
||||||
|
%INCLUDE "source/kernel/features/check.asm"
|
||||||
%INCLUDE "source/kernel/features/time.asm"
|
%INCLUDE "source/kernel/features/time.asm"
|
||||||
%INCLUDE "source/kernel/features/utils.asm"
|
%INCLUDE "source/kernel/features/utils.asm"
|
||||||
%INCLUDE "source/kernel/features/cli.asm"
|
%INCLUDE "source/kernel/features/cli.asm"
|
||||||
%INCLUDE "source/kernel/features/misc.asm"
|
%INCLUDE "source/kernel/features/misc.asm"
|
||||||
%INCLUDE "source/kernel/features/basic.asm"
|
%INCLUDE "source/kernel/features/basic.asm"
|
||||||
|
|
||||||
|
; PROGRAMS
|
||||||
|
%INCLUDE "source/kernel/programs/ed.asm"
|
||||||
|
|
||||||
; DATA/VARIABLES
|
; DATA/VARIABLES
|
||||||
%INCLUDE "source/kernel/data.asm"
|
%INCLUDE "source/kernel/data.asm"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user