answer

<< Back to posts.

How I Achieved Multitasking In SmileBASIC (Terribly)


Table of Contents

  1. My Jewel OS Project
  2. Dynamically Loading Code in SmileBASIC
    1. Loading External Program Code Files
  3. Calling Label Subroutines from Other Slots
  4. A Very Very Simple “Multitasking” Loop
  5. Setting Variables in Different Slots
  6. Putting it All Together
  7. Reflecting on This Approach

To preface this post, you should have the context that I didn’t have internet access and was unaware of works that cover how to create OSes particularly good approach to multi-tasking, just one that I happened to create as a teenager knowing the limitations of SmileBASIC. It’s more of a capturing of what is pretty much now useless knowledge considering SmileBASIC 3 isn’t used so much anymore. If you are looking for works on OS development and to get information on how multi-tasking works, I recommend reading Operating Systems: Design and Implementation.


This blog post is more of a showcase of the advanced features in SmileBASIC 3 and how I used them to create a multitasker.

I will also say that this is a blog post that won’t contain any of the original source code or even screenshots of some of the things I am talking about, as I lost them alongside my 3DS at school.


My Jewel OS Project

When I was a teenager using SmileBASIC, the project that I was most proud of working on was my Jewel OS mock OS. It didn’t really stand out from the other mock OSs in any way except for the fact that I wanted to make it look really pretty and I wanted to make it stand out from the rest of the existing mock OSs in functionality.

img

img

I was particularly obsessed with Operating Systems in general, I used to go on WinWorld all the time and download different past versions of Windows and MS-DOS to see how they work. I found old OSes to be really interesting to see how they evolved into now. Even the name of Jewel OS is a play on the GEM desktop environment name to pay homage to it. One of the major things that completely enamoured me was the capability of the multi-tasker in an OS. When I was learning how to program using SmileBASIC, one of the things that left me the most confused with programming in general was how did the OS do multi-tasking. I understood how a program could run linearly as a single program, but I didn’t understand how multiple programs could work in tandem with one another.

Having the ego that I did, I felt a little pathetic that my mock OS was just… well, a mock OS. I wanted to make something that people would have fun using and make it something that people would want to use. I wanted it to have the unique functionality to superpower a user to run multiple things at once, or run several programs they created alongside one another. I wanted to have the potential to run a graphics editor, a code editor, a lot of things all at once.

Suffice to say, while I didn’t make it all the way in achieving this ambitious goal, I at least created a base for this system to be expanded upon into the future.

img

img

What I want to show in this blog post are the several features of SmileBASIC I utilized to make this a reality and how they all combined into making foreign programs fit into a multitasking system. I don’t think I would have the same approach today for making this multi-tasking system, but I think it’s still valuable recording how I approached it with the limitations of SmileBASIC that I naively navigated.

What this blog post won’t outline is how I made graphics related to individual programs become isolated to different windows in SmileBASIC, but this mostly was a process of replacing built-in functions for SmileBASIC in these programs to custom ones that would follow rules so that it was fit within the space of a window.

Dynamically Loading Code in SmileBASIC

One of the features of SmileBASIC that would enable this is the ability to write code in other program slots while the program is running.

img

In the bottom of the editor screenshot there, you can see the EDIT 0 button alongside 1, 2, 3. Those were the four program slots available to load programs into. This made it possible to do something like load in an external library from a file using the USE or EXEC functions.

However, you can also write into those other slots and the use the EXEC function to execute the code that exists in that slot.

As an example, here’s a program that inserts code into slot 1 dynamically and executes it.

' Begin writing to slot 1.
PRGEDIT 1
' Insert [PRINT "HELLO"] into that slot
PRGINS "PRINT "+CHR$(34)+"HELLO"+CHR$(34)
' Execute the code in slot 1
EXEC 1

img

img

Now that we’re aware of the state of the system before running it, let’s run it a couple of times and see what happens to the contents of slot 1.

img

img

img

img

We can see that after running the program two times, the PRINT "HELLO" statement gets inserted into it for each time that the program runs.

Loading External Program Code Files

As I mentioned before, you can use EXEC and USE to load programs into slots.

Let’s say that I had a file saved named OWO with the following content:

PRINT "MEOW"

I could load the program into slot 1, and then continue to run the program in slot 0 after running the contents of slot 1.

' Load OWO into program slot 1
EXEC "PRG1:OWO"
PRINT "WOW"

The output of this program would be:

MEOW
WOW

Where MEOW comes from the contents of OWO and WOW comes from the contents of slot 0.

There comes an obvious limitation from the existence of these 3 slots. How in the world are we supposed to run more than 3 programs? How did YOU run more than 3 programs.

My solution came in the form of loading the contents of the program file and loading it into the first slot.

I’m going to add a new file named MEOW and make it print out "OWO".

PRINT "OWO"

Then we can use the LOAD function to load the contents of the programs into variables and then insert them into slot 1.

LOAD "TXT:OWO", 0 OUT OWO_CONTENT$
LOAD "TXT:MEOW", 0 OUT MEOW_CONTENT$

PRGEDIT 1
PRGINS OWO_CONTENT$
PRGING MEOW_CONTENT$
EXEC 1
PRINT "WOW"

Then when we run this program, the output will be:

MEOW
OWO
WOW
PRINT "MEOW"
PRINT "OWO"

We can see that the contents of OWO and MEOW have now been combined into one file in the slot, and we can continue to load multiple programs in sequence in this way.

There’s a problem that arises with this approach however. It’s important that we are able to intentionally decide which of the two programs that we want to run. What if I loaded both OWO and MEOW and I only wanted to run the contents of MEOW? How are we even supposed to switch between the two programs in sequence?

Calling Label Subroutines from Other Slots

In SmileBASIC, you can create named labels with the @ symbols that you can can jump to using either GOTO or GOSUB.

When using GOSUB, the interpreter jumps to the code at the label while keeping track of where you originally called the GOSUB. It’s similar to calling a function in other programming languages, where the program jumps inside of the function and returns to the position it called the function after it is over. Where GOSUB differentiates, however, is that you need to explicitly jump back to that position by using the RETURN keyword.

GOSUB @OWO
PRINT "WOW"
EXIT ' Exits the running program

@OWO
PRINT "MEOW"
RETURN

@MEOW
PRINT "OWO"
RETURN

The output of the above program:

MEOW
WOW

GOTO acts similarly to GOSUB, jumping to the code at the label. Where it differs, however, is that it doesn’t keep track of the position of the program you called GOTO from. If you try to call RETURN after calling GOTO, the program won’t be able to return back to the position where GOTO was called.

GOTO @OWO
PRINT "WOW"
EXIT

@OWO
PRINT "MEOW"
RETURN

@MEOW
PRINT "OWO"
RETURN

This code example won’t work, because running RETURN without having first running GOSUB (or without it being inside of a function) will cause an exception to be raised.

I don’t want to get too caught up in the functionality of GOSUB and GOTO, what I want to focus on is a certain functionality that these functions that jump to labels have that I abused for this multitasking functionality.

img

A program SLOT can be specified in the following format “1:@Label name”

We can use this functionality to jump to labels that we define in other program slots that we generate dynamically.

Here’s an example of the program from slot 0 running subroutines from slot 1.

    ' USE loads symbols and variables from slot 1 without executing the code from the slot
    USE 1
    GOSUB "1:@MEOW"
    GOSUB "1:@OWO"
    PRINT "WOW
    @OWO
    PRINT "MEOW"
    RETURN
    
    @MEOW
    PRINT "OWO"
    RETURN

And the output of running the program in slot 0 is:

OWO
MEOW
WOW

A Very Very Simple “Multitasking” Loop

Using what’s been introduced so far, we can load multiple programs and run them in a loop with one another.

Given the previous existing OWO and MEOW files, we can load them into slot 1 and wrap them using a named label and a RETURN statement to iterate through them.

' All of the programs loaded into context
VAR PRGS$[0]

' Loads the program from a given FILENAME into slot 1 and wraps it
' with a label
DEFUN LOADPRG FILENAME$
  VAR CONTENT$ = ""
  LOAD "TXT:" + FILENAME$, 0 OUT CONTENT$
  PRGEDIT 1
  PRGINS "@" + FILENAME$ + CHR$(10)
  PRGINS CONTENT$
  PRGINS "RETURN"
  ' Push FILENAME$ into PRGS$
  PUSH PRGS$, FILENAME$
  ' Load labels from slot 1 into context, HOWEVER, we lose variable
  ' state every time we run USE, oops, that was a drawback back then too
  USE 1
ENDDEF

' Now let's load both the OWO and MEOW files
LOADPRG "OWO"
LOADPRG "MEOW"

' Now let's iterate through them
WHILE TRUE
  FOR I = 0 TO LEN(PRGS$)-1
    PRINT "Running [" + PRGS$[I] + "]"
    PRINT "-----"
    GOSUB "1:@" + PRGS$[I]
    PRINT "-----"
  NEXT
WEND

After running the program and LOADPRG runs for OWO and MEOW, the contents of slot 1 will be:

@OWO
PRINT "MEOW"
RETURN

@MEOW
PRINT "OWO"
RETURN

The output of this program will be:

Running [@OWO]
-----
MEOW
-----
Running [@MEOW]
-----
OWO
-----
Running [@OWO]
-----
MEOW
-----
...

Huzzah! We’ve created a program that can run multiple programs in round-robin fashion!

Though, this isn’t what I would qualify as effectively multitasking, however. When multitasking, you don’t really wait for an entire program to finish before switching to the other program, and you also don’t typically run an entire program when multi-tasking before switching to it again.

See, my goal when making this mock OS was that the program shouldn’t have to be specially made to work underneath the mock OS. It shouldn’t have to restrict the user to fitting everything within a single program. So, how do we keep track of the state that the program is in and jump back to the correct location when we exchange control between different programs?

Setting Variables in Different Slots

There’s a really weird feature in SmileBASIC where you can dynamically refer to a variable name using a string using the VAR keyword. It doesn’t seem to be documented when you press the ? button for VAR, but I had heard about it, being a regular user of SmileBASIC Source.

If you wanted to set a value to the variable FOO, you could use VAR like the following to set it:

' First, we need to define the variable before we can use the VAR function
VAR FOO = 0
VAR("FOO") = 21
PRINT ("FOO") - 10
PRINT FOO

The output of the program would be:

11
21

img

An even more bizarre behaviour is the fact that we can set and get the values for variables for other slots using the VAR function.

I will give an example using a program:

VAR FOO = 20
EXEC 1
PRINT FOO

VAR FOO = -20
VAR("0:FOO") = 40
PRINT FOO

After running this program, we will get the output:

-20
40

The selecting behaviour works similarly to calling labels in different slots, where we add a prefix of the slot number plus a colon, here being 0:. This gives us the ability to set variables from other slots to control global state.

Putting it All Together

Now that we have the ability to set variables in different slots, now we finally have a way to keep track of switching states in the program.

See, a trick that I employed from this is that I would create a hash for every program that was loaded in and then inject labels at specific points to yield the programs from, while setting a variable in slot 0 to set the next label to jump back to.

For example, if I had this program here:

PRINT "Hello!"

WHILE TRUE
  FOR I = 0 TO 10
    PRINT I
  NEXT
WEND

I could add a compile step to add labels that I could yield from at specific “special points” when I run the program.

@xxxxxxxx_0
PRINT "Hello!"

WHILE TRUE
  FOR I = 0 TO 10
    PRINT I
  VAR("0:PRG$")["0:PRG%"]=@xxxxxxxx_1:RETURN:@xxxxxxxx_1:NEXT
VAR("0:PRG$")["0:PRG%"]=@xxxxxxxx_2:RETURN:@xxxxxxxx_2:WEND

For each of these “special points”, I set the variables in slot 0 to save the context related to the currently running task.

Think of this being translated to:

' Affecting the variables in slot 0
PRG$[PRG%]=@xxxxxxxx_1:RETURN:@xxxxxxxx_1:NEXT

This tells our multitasker that the next time that we arrive at this program in the scheduler, that this is the label that we want to run GOSUB with again.

Our entrypoint would begin at @xxxxxxxx_0, run GOSUB to jump to that entrypoint, and have the entrypoint switch as the program traverses.

Typically, I would add this yield statement before a loop, and replace statements like WAIT with special directives, and so on.

The sequence of events would then follow as such:

@startuml
!theme vibrant
Jewel ---> Jewel : Compiles and loads Child program, setting entrypoint label to @xxxxxxxx_0
Jewel -> Child : Give up control to child (GOSUB PRG$[CHILD]) [GOSUB @xxxxxxxx_0]
Child --> Child : Reach NEXT statement
Child --> Jewel : Set new entrypoint [VAR("0:PRG$")[VAR("0:PRG%")]=@xxxxxxxx_1]
Child -> Jewel : Give up control to multitasker (RETURN)
Jewel -> Child : Give up control to child (GOSUB PRG$[CHILD]) [GOSUB @xxxxxxxx_1]
Child -> Child : Repeat 9x times
Jewel -> Child : Give up control to child (GOSUB PRG$[CHILD]) [GOSUB @xxxxxxxx_1]
Child --> Child : Reach WEND statement
Child --> Jewel : Set new entrypoint [VAR("0:PRG$")[VAR("0:PRG%")]=@xxxxxxxx_2]
Child -> Jewel : Give up control to multitasker (RETURN)
Jewel -> Child : Give up control to child (GOSUB PRG$[CHILD]) [GOSUB @xxxxxxxx_2]
Child --> Child : Reach NEXT statement
Child --> Jewel : Set new entrypoint [VAR("0:PRG$")[VAR("0:PRG%")]=@xxxxxxxx_1]
Child -> Jewel : Give up control to multitasker (RETURN)
@enduml

img

And here is that revised multitasker, adding a new PRG% variable as well as replacing LOADPRG to inject those new labels at those specific points (except I don’t feel like rewriting what is basically a decade old now!).

' All of the programs loaded into context
VAR PRG$[0]
' The index of the program we are running
VAR PRG%

' Loads the program from a given FILENAME into slot 1 and wraps it
' with a label
DEFUN LOADPRG FILENAME$
  ' Omitting as I don't feel like recreating the compiler thank you!!!
ENDDEF

' Now let's load both the CHILD file
LOADPRG "CHILD"

' Now let's iterate through them
WHILE TRUE
  FOR PRG% = 0 TO LEN(PRGS$)-1
    PRINT "Running [" + PRGS$[PRG%] + "] [" + PRG% + "]"
    PRINT "-----"
    GOSUB "1:@" + PRGS$[PRG%]
    PRINT "-----"
  NEXT
WEND

After loading the program and beginning the multitasker, now we get this:

Running [@xxxxxxxx_0] [0]
-----
Hello!
0
-----
Running [@xxxxxxxx_1] [0]
-----
1
-----
Running [@xxxxxxxx_1] [0]
-----
2
-----
Running [@xxxxxxxx_1] [0]
-----
3
-----
Running [@xxxxxxxx_1] [0]
-----
4
-----
Running [@xxxxxxxx_1] [0]
-----
5
-----
Running [@xxxxxxxx_1] [0]
-----
6
-----
Running [@xxxxxxxx_1] [0]
-----
7
-----
Running [@xxxxxxxx_1] [0]
-----
8
-----
Running [@xxxxxxxx_1] [0]
-----
9
-----
Running [@xxxxxxxx_1] [0]
-----
10
-----
Running [@xxxxxxxx_1] [0]
-----
-----
Running [@xxxxxxxx_2] [0]
-----
0
-----
...

Huzzah! Now we actually have something that CAN multitask and actually keep state of where it’s at in the program when switching between programs.

Theoretically, if we loaded a CHILD2 after loading CHILD1, and it generated the labels with yyyyyyyy to differentiate the two from one another (and also used J as the loop variable, I think in my compiler, I also renamed common variables like that so there wouldn’t be any conflicts, but it’s been so long ago…), we could get an output like this.

Running [@xxxxxxxx_0] [0]
-----
Hello!
0
-----
Running [@yyyyyyyy_0] [1]
-----
Hello!
0
-----
Running [@xxxxxxxx_1] [0]
-----
1
-----
Running [@yyyyyyyy_1] [1]
-----
1
-----
...

Now we have multiple programs running in combination with one another, being able to remember which place they are in, and we also have an “OS” surrounding the tasks that exist.

Reflecting on This Approach

Without even knowing it, I had long ago created a form of yielding multitasker where program explicitly gives up control to the scheduler.

While we are able to multitask using this label based solution, there are a multitude of issues that come with this approach:

  1. Every time we load a new program and run USE, the variables in the slot we run USE for get reset, and therefore we lose necessary context for those programs.
  2. It’s very easy for there to be clashing variable names between multiple programs.
  3. This “label jumping” approach doesn’t work very well for programs that are made using subroutines made with DEFUN. We can’t jump into labels inside of these kinds of functions.

And there’s probably many more that I haven’t thought of.

The most correct solution that I can imagine working is using a virtual machine to keep track of program state and to run programs in an isolated environment. I suggest this because I draw inspiration from how modern OSes interrupt programs and switch context between one another, running programs for a set quantum before a clock forces the program to switch back to the scheduler. This scheduler would then decide which program to switch into. This virtual machine approach wouldn’t need me to insert this RETURN statement at all, it would instead detect how much time has passed, save the context of the program (such as local variables, graphics, sprites, etc), and then the scheduler for the virtual machine would decide which program to switch into next.

However, I think that the way that I approached multi-tasking is a showcase of some pretty advanced and niche features in SmileBASIC, and for that alone, I think that the multitasker I made so long ago has value.

I have thought about this, recently, about how with the nature of my work, I won’t often be in a position to come up with clever and stupid solutions to problems. This mostly comes from the fact that I’m no longer working in a restricted interpreter made for the Nintendo 3DS and the fact that front facing web development doesn’t reward this kind of development, so much. I think, however, that this environment and being ambitious allowed me to come up with a really stupid but neat solution to a problem that’s been solved in so many better ways, and that alone, is fun and nice to think on.

Not to mention, now I was able to make a dumb blog post about all the techniques I did to make it happen. So that’s nice.