-
Notifications
You must be signed in to change notification settings - Fork 15
Getting Started
Until a filesystem can be implemented, all AtlasOS programs must be assembled into the kernel. Below is a short tutorial on how to do so.
All applications are stored between the :apps and :apps_end labels, immediately after the :kernel_end label. Each app exists between as least two labels :appname and :appname_end. Let's take the simplest possible application as an example:
:helloworld
SET A, helloworld_string
JSR text_out ; text_out prints the contents of the A register. More on APIs later.
JSR proc_kill_me ; allow the kernel to terminate the process.
:helloworld_string dat "Hello World!", 0xA0, 0 ; all data is stored in labels. 0xA0 is a carriage return.
:helloworld_end
Now, add a new label at the bottom of the list between :application_table and :application_table_end like so:
:app6 dat "helloworld", 0, helloworld, helloworld_end
Note that the first parameter, "name_of_app" can be completely arbitrary. Assemble the kernel, run it, and type "load helloworld" to see the result:
$> load helloworld
Hello World!
$>
Due to the lack of interrupts or an MMU on the DCPU, multitasking on AtlasOS is entirely cooperative. The example above actually doesn't cooperate, and the kernel can do nothing about it. A cooperative app yields to other processes by calling a special subroutine within the kernel. If our app is going to run for longer, chances are it will be built in a big loop, so it follows that we would yield at the end of each iteration:
:helloworld
SET I, hello_loop_end
SUB I, hello_loop ; define memory boundries of the loop to follow.
SET J, 4 ; defines the number of iterations for the loop to follow.
:helloworld_loop ; here we have added a loop.
SET A, helloworld_string
JSR text_out
SUB J, 1 ; subtract 1 from the counter after each iteration.
IFE J, 0
JSR proc_kill_me ; when the counter hits zero, kill the process.
JSR proc_suspend ; otherwise, yield to the kernel.
SUB PC, I ; the kernel will call us back here, so we jump back to the top of the loop.
:helloworld_loop_end
:helloworld_string dat "Hello World!", 0xA0, 0
:helloworld_end
Assemble, run, and load as before to see the result:
$> load helloworld
Hello World!
Hello World!
$> Hello World!
Hello World!
Uh-oh, that wasn't quite the result we were hoping for, was it? The loop printed "Hello World!" four times like we expected, but something else seems to have gone wrong. It's actually quite simple: the shell thought our app was done running, and started printing to the screen before we were done! The biggest pitfall of multitasking is having to deal with other apps consuming resources and doing unpredictable things. Not to worry, though, there is a simple way to work around the shell. If we kill the shell, the kernel will wait until our app has finished executing to revive it:
:helloworld
SET A,2 ; the shell should always be number 2 on the process list, after the kernel.
JSR proc_kill ; proc_kill kills the process in the A register. More on APIs later.
SET I, hello_loop_end
SUB I, hello_loop
SET J, 4
:helloworld_loop
SET A, helloworld_string
JSR text_out
SUB J, 1
IFE J, 0
JSR proc_kill_me
JSR proc_suspend
SUB PC, I
:helloworld_loop_end
:helloworld_string dat "Hello World!", 0xA0, 0
:helloworld_end
Assemble, run, and load as before to see the result:
$> load helloworld
Hello World!
Hello World!
Hello World!
Hello World!
Atlas-Shell v0.3.2
$>
As you can see, the shell has started fresh complete with the version splash, and it did so seamlessly. It is also possible to restart the shell without the version splash, to clear the command history from the screen, and much more. More on that later, in the next section on:
// TODO