online: 1
Rekord: 22

Last update:
2005/03/19

Home
Search:
Suche starten
Bitte in Deutsch!

RXE execution speed

Usually flash devices have higher access times than RAM devices do. So it's unavoidable that RXE programs will be slower in execution than regular EXEs are as the CPU permanently has to read from the flash. Also Oliver Coupelon makes us fear with a little statement in his Programming Guide: "... But it also has a significant defect, that to slow down the speed of execution of the programs".

But in fact, this defect isn't that tragically.

I was very curious about the execution speed of programs running on the flash. When I tested it, the result was great, much better than I expected: it was 88% that of programs running in the RAM (this result should be very accurate, as time was stopped by interrupts for measurement).

So we cannot use RXE for games only that need lots of memory and not much CPU ressources (such as big RPGs and Adventures), but for games needing high performance, too, such as Jump'n Runs or 3D games (RXE slows down about 13% only).

And there's even a way how to boost up an RXE game's speed:

Most CPU ressources fall apart for graphical routines, especially such as block filling/moving (HLine/VLine/SpriteCopy and so on). What about copying these critical routines into RAM during runtime and call them via procedural variables then? It will not take much memory if only these critical routines are copied, but they will be executed the fast as in EXE format.

First of all, for programs written completely in assembler this is very simple. Dscoshpe reminded me, that code directly compiled into the data block of an RXE program automatically will be loaded into RAM on startup. This, however, is possible with assembler programs only (just post your code behind the .data - directive), as higher languages such as Pascal and C don't provide a way to link procedures into data block.

For Pascal and C it works that way:

Here we really have to allocate a buffer in RAM during runtime and copy the critical procedures there. It's complicating now that sizeof() directive doesn't work for procedures (in Pascal at least), but that's no problem.

We can use the fact that the compiler if it links a procedure (attention: if a procedure is declared but never referenced in the source, compiler will remove it), links it in exactly that order as declared in the source code. Thus, if we declare procedures to be copied to RAM in a continuous block, they also will be arranged in a continuous block in memory simplifying things much.

For determining a procedure's offset within that block we just need to subtract the block's first procedure's offset from this procedure. Determining the blocks size, we mark it's end with a dummy procedure and do the same. After copying the procedures, we can set the procedural variables in order to call them. The source code looks this like (for Pascal; similar in C):
{procedural variables}

var hline: procedure(x,y,w: integer);
    vline: procedure(x,y,h: integer);
    ...

{block with critical procedures starting here}

procedure hlinecritical(x,y,w: integer); far;
begin
 ...
end;

procedure vlinecritical(x,y,w: integer); far;
begin
 ...
end;

...

{this marks the block's end, we need it as sizeof doesn't work here}

procedure dummy;
begin
end;

{buffer to copy critical procedures to}

var criticalprocs: pointer;
    criticalprocssize: word;

begin

  {allocate buffer & copy procs to RAM}
  criticalprocssize := ofs(dummy)-ofs(hlinecritical);
  getmem(criticalprocs,criticalprocssize);
  move(ptr(cseg,ofs(hlinecritical))^,criticalprocs^,criticalprocssize);

  {initialize procedural vars}
  hline := criticalprocs;
  vline := ptr(dseg,word(criticalprocs)+ofs(vlinecritical)-ofs(hlinecritical));
  copysprites := ptr(dseg,word(criticalprocs)+ofs(copyspritescritical)-ofs(hlinecritical));
  ...

  {And now, we just need to call hline, vline, copysprites, ... variables}
  {They will be called from RAM!}
end.
This method has been tested and it works

When putting out the first message, CS=16491 meaning the program starts at address 16*CS = 406B0. As this is beyond RAM area 00000..3FFFF in adress space, we see that code currently is executed from flash. The program copies a testing procedure to RAM and calls it putting out the second message (with CS=3325 wich is inner RAM).

The RAM procedure returns back to flash, the program gives an OK message and exits.

Only two things you have to watch out for:

Of course the compiler assumes these procs copied to RAM later would be in original CS.
  1. Thus, calling non RAM procs from a RAM proc defined in the same CS will not work, unless you also call them by procedural variables (of course you have to declare all RAM procs as far). No matter for procs defined in another unit (also system unit), as these have another CS anyways. For C this should be similar as in Pascal.

  2. Data constants such as arrays or strings usual are putted to the code segment causing trouble too (you can't access them from procs copied to RAM). Solution: make them predefined variables instead of constants, that'll force the compiler to put them into data segment (DS of course stays untouched when calling a RAM proc).

    Example (assume test to be called from RAM):
    procedure test; far;
     writeln('Hello world from seg ',cseg,'!');
    end;
    will not work, but
    const s: string = 'Hello world from seg ';
    
    procedure test; far;
     writeln(s,cseg,'!');
    end;
    works fine.
Copyright (C) 2005 by Marco Kaufmann