[Tool]FemtoIDE

Works! So what did I do wrong?

Unless we can come up with a one-size-fits-all format for tilemaps, the conversion is done by a script in the project (scripts/tilemap.js), not by the IDE itself. When you tried to import the TMX file, the IDE doesn’t know what to do with it so it was getting imported as a raw binary.
To make it work I added the missing script and added the “pre-build” step in your project.json.

1 Like

Could the IDE make some default script on import of the tmx file? The size information is in the tmx-file.

Size information, as in, the size of the map? Is that of any use without the map itself?

I do not have studied the details of the map generation from tmx-file, but just thought if having at least some javascript file as a default (created during importing a tmx file) would be a good idea.

Fixed this bug in tilemap.js:

    int read${layer.name.replace(/[^a-zA-Z0-9_]/g, '')}(int tileX, int tileY){
        pointer addr = this.data + ${4+num*(1+map.width*2*map.height)} + tileY*${map.width*2} + tileX*2;
        return (((int)LDRB(addr)&1)<<8) + LDRB(addr+1);
    }

There was
this.data + ${3
instead of
this.data + ${4

1 Like

Femto v0.1.0: Compiler errors are much better now :+1:

2 Likes

This doesn’t sound right… does that work on more than one layer?

Edit: I don’t have anything to test this with right now, but it should probably be this instead:
pointer addr = this.data + ${3+(num*(1+map.width*2*map.height)||1)} + tileY*${map.width*2} + tileX*2;

(note the ||1)

1 Like

The next version of FemtoIDE will bring initial support for PokittoCookies in Java.

For now, cookies can store any primitive type (char, int, long, float, boolean) but nothing that extends Object (classes including String and Array).

For those who are on the bleeding edge, here’s an example of it in use:

import femto.mode.Direct;
import femto.Game;
import femto.State;
import femto.input.Button;
import femto.palette.Psygnosia;
import femto.font.TIC80;

class Score extends femto.Cookie {
    Score(){
        super();
        begin("test"); // name of the cookie, up to 8 chars
    }
    int score; // properties you want to save
}

class Main extends State {
    static final var save = new Score(); // previous value is automatically restored
    static final var screen = new Direct(TIC80.font()); // the screenmode we want to draw with

    public static void main(String[] args){
        Game.run( TIC80.font(), new Main() );
    }
    
    void init(){
        screen.clear(0);
    }

    void update(){
        if( Button.A.justPressed() ){
            save.score = Math.random(0, 1000); // update the score
            save.saveCookie(); // save it to eeprom
        }

        screen.setTextPosition( 0, 0 );
        screen.textColor++;
        screen.println("Score: "+save.score); // print the current score
    }
    
}
4 Likes

Any way to change the default path of the projects folder? I use more than one computer to dev (one being a VPC) but would like to point them both to the same shared path on my nas/dropbox and when I tried making a shortcut path, the IDE didn’t pick it up.

1 Like

Look for projectsPath in your config.js and change the value to the path you want.

2 Likes

PSA - until further notice, do not use new to initialize non-static properties. Like this:

class Fridge {
  float temperature = 15.0f;
  Bacon bacon = new Bacon(); // BAD!
}

Do this instead:

class Fridge {
  float temperature = 15.0f; // OK!
  Bacon bacon;
  Fridge(){ bacon = new Bacon(); } // OK!
}
Details...

Basically, when you initialize a property, it creates a constructor like this:

Fridge::Fridge() : bacon(new Bacon()) { }

The problem happens if the new triggers the garbage collector before bacon is initialized. It’s a dangling pointer at that moment. The GC will see the (possibly non-zero) pointer and try to access it.

Ideally, it would generate this instead:

Fridge::Fridge() : bacon(nullptr) { bacon = new Bacon(); }

Another option would be to zero memory inside new.

1 Like
Rambling...

The former should be equivalent to bacon.ctor(new Bacon()), which should call (the equivalent of) either Bacon*::Bacon*(Bacon * &&) or Bacon*::Bacon*(const Bacon * &).

The latter should be equivalent to bacon.operator=(new Bacon()), which should call (the equivalent of) either Bacon*::operator=(Bacon * &&) or Bacon*::operator=(const Bacon * &).

Either way new Bacon() should have a type of Bacon * &&.

The only thing I can think that would make a difference is that the former is technically direct initialisation (6) and the later is technically copy initialisation (1).
But even then I would have thought it would still be creating a temporary before assigning.

Is it not possible to make the allocator temporarily treat the pointer whose new triggered the GC as having an extra reference until the sweep is finished?

E.g.

void * operator new(std::size_t size)
{
	void * pointer = allocate(size);
	if(needSweep())
	{
		addRef(pointer);
		sweep();
		removeRef(pointer);
	}
	return pointer;
}

That said, does:

// Java land
class Fridge
{
	Bacon bacon = new Bacon();
}

Actually translate to this?

// C++ land
class Fridge
{
	__ref__<Bacon> bacon = new Bacon();
};

It translates to this, simplified:

class Fridge
{
	Bacon *bacon = new Bacon();
};

__ref__ is only used for static variables. Properties and local variables use actual pointers.

The problem isn’t in the form of initialization (copy vs assign) but the fact that the Fridge class has an invalid pointer (bacon) during a sweep.

1 Like

Oh I see, I got confused between bacon and new Bacon().

Presumably that means:

// Java land
class Fridge
{
	Bacon bacon;
	Fridge() { bacon = new Bacon(); }
}

Translates to:

// C++ land
class Fridge
{
	Bacon bacon = nullptr;
	Fridge() { bacon = new Bacon(); }
};

(I’m presuming that because I think Java would require that bacon were null before the body of Fridge() begins, but it’s been quite some time since I’ve used Java for any extended period.)

In which case I think the only thing you can do is to change how the default assignment of a member variable in Java is translated to C++ (which would mean more work for the parser/translator).

Unless there’s some way you could ‘hide’ the mid-construction Fridge object from the system during initialisation, and then 'reveal it once it’s fully constructed, but that also sounds like a lot of work, and is possibly the more awkard option.

1 Like

Correct.

Yep, that was the first option I mentioned (initialize with null then put the actual initialization expression in the beginning of all constructors).

I’ll try the other option first, since it has potential to take up less flash space:

void *operator new(size_t size)  {
    auto c = (int*) __wrap_malloc(size);
    auto i = c;
    while(size-=4) *i++ = 0;
    return c;
}
1 Like

My performance instincts are cringing,
but I suppose technically Java would actually be doing something similar to this anyway.
(At least I presume so, I never read Java’s rules that in depth.)

Mine did some cringing too, but this is probably better performance-wise than setting each object’s properties to zero individually. Even if it weren’t, the cost of simply using new (making malloc go through a linked list looking for free RAM) is probably going to be much higher anyway, so it shouldn’t be a problem.

1 Like

Would anybody mind if Java had overclocking always enabled, like in MicroPython?

I kind of like having it as an optional thing.

2 Likes