ABA Games

もっとメモリを


サイズ削減のための闘い

ver.0.01で分かったこと。とにかくメモリが足りない。 本体だけで34Kも使っているようじゃ使いもんにならん。 なりふり構わずメモリ削減に走らなければ。

とりあえずどこから毒牙にかけるべきかというと、

ファイラー予約エリア

かな。まず。

ラピュータ開発のことならばここが必見 Ruputer Fanの中のドキュメント、 メモリマップ上の予約エリア を見てもらうと分かりますが、ラピュータには「ファイラー予約」なる謎の エリアがユーザエリアの前にあります。その量3.5KB。3.5KBなんて、普段だったら 蚤の涙なみのどうでもいい大きさですが、ラピュータではとんでもなく貴重です。

そしてこのエリア、どうも「予約されてない」らしいのです。 少なくとも現状のラピュータでは使ってしまっても問題がない。 将来的に問題が発生するかも知れませんが、「ラピュータにはもう将来はない」 ので(ひどい)、容赦なく利用します。

このエリアを使うようにするのは簡単で、CodeWarriorの場合、 Panasonic Linkerの設定の、

-Ttext 0x110100
-Ttext 0x10F300
にするだけです。これで3.5KBは君のものだ!

mallocとfree

次にどうにかするのがこいつら。 ver.0.01では、メモリの確保(malloc)と解放(free)の 実現方法は、nmruputer_a.cにあるように、以下の通り。
#define xmalloc(size) malloc(size)
#define xfree(ptr) free(ptr)
まんまです。これで「礼儀正しい」mallocができます。 ですが、Wabaを実装するときには、こんなにちゃんとした、 メモリを浪費するmallocはいりません。

なぜかというと、「Wabaはその一生の中で4回しかメモリを確保しない」 からです。その4回で確保するのは、

です(参考 dealing with memory)。

Wabaは、この4つを確保したあとはメモリ管理を自前で行います。 ですので、この4つが最初に確保されると、あとは自分が死ぬまでこれらを 放さずに使い続けます。

(Waba4Ruputerではあと一回、CLASSES.WRPファイルを読み込むときに mallocをしていますが、それでも計5回です。)

よって、mallocは、とにかく指定された量の連続したメモリを確保できればよく、 freeにいたっては実装する必要すらありません。すると、こいつらは 以下のように実装できます。

static uint32 mlcAddress = 0x116400;

static void *xmalloc(uint32 size) {
	uchar *ptr = (uchar*)mlcAddress;
	mlcAddress += (size&0xfffffff0)+16;
	return ptr;
}

static void xfree(void *p) { }
なかなかに横暴な作りですが、ラピュータ上ではうまく動いている、 ように見えます。ちなみに、メモリを切り出す先頭アドレス(0x116400)は どうやって決めるかというと、CodeWarriorがmakeの際にexfファイルと いっしょに吐き出すmapファイルを見て決めます。

mapファイルとは、生成したexfファイルがどのようにメモリマップ上に 配置されるかを記したファイルです。 ちなみにWaba.mapの一部を抜粋すると、

 COMMON 0x001162fa 0x2 C:\Metrowerks\Ruputer\RupSDK\lib\libpsdos.a(doserr.o)
        0x001162fa         DOSerr
        0x001162fc         _end=.
        0x001162fc         PROVIDE (end, .)
となっているので、どうも0x001162fcまで使われているらしい。 ので、0x116400からmallocしてしまえ、ということで先頭アドレスが 決まっています。いい加減だけど、とりあえず動くし。

(3/30現在、これで本当に正しいのかちょっと自信がない。たまにスーパークラスの クラス名の取得に失敗することがあり、その原因がこのいいかげんなメモリ取得の せいかもという気がちょっとします。)

小数禁止

次。とうとうVMのスペックに手をあげることにする。 floatのサポートをやめてみます。小数が分からないVM。

さすがにこれはおいそれと実行しづらく、後で戻せるようにしておきたいので、 UNSUPPORT_FLOATというのがdefineされていると、floatがサポートされないように なっています。詳しくはwaba.c参照。

具体的には、以下のJavaVM命令セットを無視します。

主な変更は、waba.cのexecuteMethod関数にしてます。 それ以外にもちょこちょことした変更はありますが。

ここまでやって

てな感じ。ver.0.01が34,400バイトだったので、約6.5KBの縮小。 あと、ファイラー予約部分を3.5KB侵食したので、合計ちょうど10KB 儲かった計算になる。これは大きい。

新サンプル

として、ラインが動きまわるものを作ってみました。
import waba.fx.*;
import waba.ui.*;

public class MainClass extends MainWindow {
  private final int FLOW_LINE_NMB = 10;

  private FlowLine[] fl = new FlowLine[FLOW_LINE_NMB];
  private int flIdx;

  private int x1, y1, x2, y2;
  private int mx1, my1, mx2, my2;

  public void onStart() {
    flIdx = 0;
    x1 = Rnd.nextInt()&(63*256); y1 = Rnd.nextInt()&(63*256);
    x2 = Rnd.nextInt()&(63*256); y2 = Rnd.nextInt()&(63*256);
    mx1 = Rnd.nextInt()%768; my1 = Rnd.nextInt()%768;
    mx2 = Rnd.nextInt()%768; my2 = Rnd.nextInt()%768;
  }

  private final int WIDTH = 102*256, HEIGHT = 64*256;

  public void onPaint(Graphics g) {
    for ( int i=0 ; i<32 ; i++ ) {
      if ( fl[flIdx] != null ) {
        fl[flIdx].clear(g);
      } else {
        fl[flIdx] = new FlowLine();
      }
      fl[flIdx].setPos(x1/256, y1/256, x2/256, y2/256);
      fl[flIdx].draw(g);
      x1 += mx1; y1 += my1;
      if ( x1 < 0 || x1 >= WIDTH ) {
        mx1 = -mx1; x1 += mx1*2;
      }
      if ( y1 < 0 || y1 >= HEIGHT ) {
        my1 = -my1; y1 += my1*2;
      }
      x2 += mx2; y2 += my2;
      if ( x2 < 0 || x2 >= WIDTH ) {
        mx2 = -mx2; x2 += mx2*2;
      }
      if ( y2 < 0 || y2 >= HEIGHT ) {
        my2 = -my2; y2 += my2*2;
      }
      flIdx++; if ( flIdx >= FLOW_LINE_NMB ) flIdx = 0;
    }
  }
}
ですが、これ、とても「動きまわる」と言えるほどの動作をしない。 端的に言って、「むちゃくちゃ遅い」。

今後

ラインサンプルを作って分かったのですが、このVM「遅すぎ」ます。 性能の向上が図れるといいのですが、アセンブラにまで手を出す気はないし。 とりあえずQUICKBINDの復活かなあ。


Rerurn もどる