Extensible Loader

Oops. Yea. I guess you will not be changing that bit.

Do you know how to debug with 2 symbol sets? (ofcourse you do)

1 Like

In the command line GDB I use:
add-symbol-file "path/to/second/file.elf" address

Good. Debugging a loader is a bit trickier than just one application.

I also used command-line GDB for developing the loader, although I figured out how to do it in EmBitz also

1 Like

Indeed. Having to copy builds to the SD each time is getting pretty annoying. :stuck_out_tongue:

Know how when you do a lot of work on a project you get this urge to share screenshots to show how things are moving along?

1

Heh. Yeahā€¦

Oh well, hereā€™s whatā€™s going on under the hood:

  • Game calls loader like normal
  • Loader puts kernel (boot.pop) in RAM, occupying ~2kb.
  • Kernel supports up to 8 independent ā€œprocessesā€ with cooperative, round-robin multitasking in RAM.
  • When no processes are running (either because it just booted or because they all exited), the kernel spawns the desktop app to RAM.
  • Processes can call and kill each other.

I keep saying ā€œin RAMā€ a lot to emphasize that the flash has remained untouched, so far. I gave up on the idea of having a rigid memory layout: processes can freely occupy up to 29kb of RAM in 1kb pages. If two processes try to occupy the same page, the oldest gets killed.

The cooperative multitasking and IPC should allow us to have a plain progress bar or simple minigames while the loader is copying to flash without significantly slowing things down.

The screen mode got moved out of the kernel and into the applications. I did this so that we could have different screen modes, depending on the needs of each app:

  • The POP loader will likely require mode 2
  • A TXT loader needs mode 1
  • BMP loader will draw directly to the screen
  • The BIN/MPY/GB loaders wouldnā€™t have icons to show, so the Amiga/POKITTOS UI works well.

Because of this flexibility, we can have minigames/apps that get executed entirely in memory. Might make for some interesting demos, screensavers and utilities.

3 Likes

Good progress!

It would be much faster to have loaders in ram than to flash them to rom.

Co-op. MT sounds awesome! I can imagine loaders having own progress bars, like mario running for coins :wink:

Maybe they could have, if supported by the loader. Like a python loader would search icons & screenshots from the ā€œpy-iconsā€ folder.

1 Like

Not a slightest idea what the heck youā€™re talking about. Donā€™t look at my raycasting thread BTW :smiley:

1 Like

Wow, I made myself skim through the thread and seriously hats off @FManga. A freaking microkernel kind of operating system on Pokitto. Itā€™s potentially becoming more than a simple gaming system. If I have some time, Iā€™ll try to create some skin mockups (warning: may include images of wildbeasts and penguins :smiley:)

3 Likes

Yes, please post mockups, otherwise youā€™ll be seeing more of my programmer art. :laughing:

Speaking of programmer art, hereā€™s the completed (for now) desktop:
desktop

The desktop application scans the ā€œ/loader/desktopā€ folder and streams the icons from the pop files into a scrollable list that can be up to 65535 items longā€¦ but ideally should only contain about 5 or so. Since itā€™s supposed to be a short list, I used big (36x36 @ 16colors) icons.

The icons on the desktop use this palette.

Next:
Once you select an item, the desktop application will close and that item will be loaded. The first item to implement will be the bin/pop loader. It will have a different interface* and support directory navigation so you can organize your games.

  • I havenā€™t actually decided which interface. We should make a poll.

To-do: For version 2 Iā€™d like to use the desktop as a list of ā€œfavoritesā€. If you like something, you can copy it to the desktop folder and it will open with the associated loader. This is for later, though. For now, the desktop supports POP files only.

8 Likes

Looks very good :-)I like the big icons. Adding favourites is a fine idea also.

It looks a bit like NIntendo 3DS menu. Maybe Pico-8 palette would be nice.

2 Likes

I like itā€¦ almost reminds me Emulation Stationā€¦ Could just add how many games are under each section and itā€™s almost the same :wink:

1 Like

Good idea !

I chose this one because itā€™s harder for me to make something terribly hideous with the limited amount of colors. :stuck_out_tongue:

Hmmā€¦ Iā€™ll have to think about this a bit. :thinking:

Pokitto Mint:

image

I basically redrew what I see on my desktop. CC0.

Would be cool if you could create plugin ā€œā€ā€œappsā€"" for the bottom bar.

4 Likes

I implemented support for desktop plugins.
The desktop looks in ā€œloader/desktopwidgetsā€ and runs all the pops inside. Widgets can customize the desktop app by loading a background wallpaper, changing the palette, printing the time/battery level, etc.

A boring/trivial ā€œhello worldā€ example:
1

The counter on the bottom left increments each time I press left/right. The compiled widget is just 340 bytes (352 in pop format). Hereā€™s what the code looks like:

#include "../kernel/kapi.h"

namespace DESKTOP {
    #include "../desktop/api.h"
}

DESKTOP::API *desktop;
int c, pressed;

void setup( DESKTOP::API *dtapi ){
    desktop = dtapi;
}

void loop( KAPI *kernel ){
    if( isPressedLeft() || isPressedRight() ) c += pressed?0:++pressed;
    else pressed = 0;
    desktop->setCursor( 0, DESKTOP::height-5 );
    *desktop->drawcolor = c&0xF;
    desktop->printf("%d", c);
}

6 Likes

Can dtapi and kernel be nullptr?
If not, references would be better than pointers.

Also why setup and loop?
Seems a bit odd in a non-Arduino environment.

Not in this particular case, but there are enough situations where an api can be null that I preferred the uniformity of just using pointers. Itā€™s not set in stone though, Iā€™m going to have to clean up before I make a release and Iā€™ll reconsider it.

I thought it seemed fitting/convenient considering how simple desktop widgets are supposed to be.
What would you suggest instead?


3
Same desktop, customized with a plugin:

#include "../kernel/kapi.h"

namespace DESKTOP {
    #include "../desktop/api.h"
}

void setup( DESKTOP::API *desktop ){
    
    desktop->itemStrideX = 0;
    desktop->itemStrideY = 42;
    desktop->itemOffsetX = DESKTOP::width - 38;
    desktop->itemOffsetY = -8;
    desktop->moveX = 0;
    desktop->moveY = -40;
    
    desktop->lblX = DESKTOP::width - 20;
    desktop->lblY = 28;
    desktop->lblColor = 0;

    desktop->clearColor = 1;
    desktop->clearX = DESKTOP::width - 40;
    desktop->clearWidth = 40;
    
    FS.init("");
    
    FILE *f = FS.fopen("loader/wallpaper.16c", "r");
    if( f ){
	
	FS.fread( desktop->screenbuffer, 1, 55*88, f );
	FS.fclose(f);
	
    }

}

4 Likes

Iā€™m OK with that.

ALSO: Impressive work so far Felipe!

1 Like

If there are provable cases where it makes sense for the API to be nullptr then it makes sense to use pointers.
But that means everyone will always have to check that the pointer isnā€™t nullptr as a precaution.

If there arenā€™t any situations where the API can be nullptr then references make more sense.

initialise and update (or init and update if you want to avoid the -ise vs -ize debate, though itā€™s not hard to make initialize an alias for initialise).


Essentially I would expect usage more like:

#include "../kernel/kapi.h"
#include "../desktop/api.h"

using KernelApi = Kernel::API;
using DesktopApi = Desktop::API;

int c = 0;
int pressed = 0;

void initialise(DesktopApi & desktop)
{
	(void)desktop;
}

void update(KernelApi & kernel, DesktopApi & desktop)
{
	(void)kernel;

	if(isPressedLeft() || isPressedRight())
	{
		if(pressed > 0)
		{
			++pressed;
			c += pressed;
		}
	}
	else
	{
		pressed = 0;
	}
		
	desktop.setCursor(0, (Desktop::height - 5));
	desktop.drawcolor = (c & 0x0F);
	desktop.print(c);
}
#include "../kernel/kapi.h"
#include "../desktop/api.h"

void initialise(Desktop::API & desktop)
{
	(void)kernel;
	
	// No nullptr check required
	
	desktop.itemStrideX = 0;
	desktop.itemStrideY = 42;
	desktop.itemOffsetX = (Desktop::width - 38);
	desktop.itemOffsetY = -8;
	desktop.moveX = 0;
	desktop.moveY = -40;
	
	desktop.lblX = (Desktop::width - 20);
	desktop.lblY = 28;
	desktop.lblColor = 0;

	desktop.clearColor = 1;
	desktop.clearX = (Desktop::width - 40);
	desktop.clearWidth = 40;
	
	FileSystem::init("");
	
	FileReader file = FileSystem::openRead("loader/wallpaper.16c");
	if(file.isValid())
	{	
		file.read(desktop->screenbuffer, 1, 55 * 88);
	}
	// Let file's destructor close the file, as per RAII
}
1 Like

Yes, checking for nullptr can be cumbersome, Iā€™ll probably switch to references where it makes sense to do so later on.

Right now, simply using pointers everywhere makes things easier to write: donā€™t have to try to remember ā€œwas this supposed to be a pointer or a reference?ā€ and I donā€™t have to think about all the possible future cases (will I ever want to change this to point to something else?).

Later on Iā€™ll go over it all and make it easier to read.

I like init and update.

The names can be changed freely by the widgetā€™s boilerplate without impact to anything else, so one widget can use init and update while another uses Widget::Widget(DesktopAPI &, KernelAPI &) and void Widget::update() or something else. All the kernel wants is function pointers.

Iā€™ll make some template widgets that use different API styles so that others can just duplicate the one they want to start off with.

A class-based FILE wrapper with RAII is in my to-do list. Just havenā€™t had the chance to get to it yet.