Extensible Loader


#61

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

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


#62

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


#63

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


#64

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


#65

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.


#66

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.


#67

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


#68

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:)


#69

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.


#70

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.


#71

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:


#72

Good idea !


#73

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:


#74

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.


#75

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);
}


#76

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.


#77

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);
	
    }

}


#78

I’m OK with that.

ALSO: Impressive work so far Felipe!


#79

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
}

#80

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.