[Tool]FemtoIDE

tool
release
open-source

#261

I should probably make that the default in the next release. It will prevent this sort of surprise happening again and people can opt-in to -O3 when needed instead.


#262

Works! So what did I do wrong?


#263

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.


#264

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


#265

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


#266

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.


#267

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


#268

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


#269

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)


#270

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

Hello
Hello
#271

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.


#272

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


#273

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.


#274
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();
};

#275

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.


#276

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.


#277

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

#278

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


#279

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.


#280

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