月別アーカイブ: 2009年2月

BASICコンパイラと手書き原稿の時代

久しぶりに思い出したのは、昔作った BASIC コンパイラが欲しいとのメールを
いただいたからです。雑誌に掲載されたのが 1989年なので 20年くらい前になります。

コンパイラといっても、当時の SHARP ポケットコンピュータ PC-1261/62 用に
見よう見まねで作ったもの。
名前は「 LC-3 コンパイラ 」といいますが、今検索で引っかかる
LC-3 compiler とは名称が同じだけで別物です。

なにせ RAM も 10.4KB と小さく、外部記憶装置はカセットテープしかありません。
そのかわり RAM はバッテリーバックアップされており、電源を切っても中身が
消えずに保持されます。

BASIC プログラムは中間コードでメモリに格納されています。それを読み出して
機械語に置き換えるわけです。エディタと字句解析に BASIC を使っているだけなので、
中間コードやキーワードを流用しつつも構文や命令は独自で完全互換ではありません。
知識も無かったので本当に単純なものでした。
その代わりメモリ容量との戦いがあります。一晩考え抜いて 1byte 削っては喜んで、
といった感じに。

資料がないので記憶だけですが、中間コードのソース領域が 3KB くらい、コンパイラ
本体が 2.0KB、ランタイムルーチンが 1.5KB、変換後のバイナリが 3KB くらいの割り
当てだったと思います。

一式全部オンメモリで動作するので、一度入力すればカセットデッキが無くても
プログラムを書けるし、そのまま動作もデバッグもできるのが特徴です。

当時すでにポケコン向けの BASIC コンパイラはいくつか発表されていました。
PiO の「ポケコンパイラ」や The BASIC のコンパイラ(正式名称失念)など。
さらにメモリが少ない RAM 4.2KB PC-1251 で動作しており、どれも出たときは
かなりすごいと思ったものです。

生成コードが洗練されていなくても BASIC インタプリタと比べるとかなり高速に
動作しました。たとえばポケコンの BASIC は整数型が無く、全部 BCD の 10進数演算が
使われています。変数 1つで 8byte。今だと double と同サイズですがポケコンでは
これでも単精度です。2進化の誤差も発生しない代わりに、ただのループ変数の
インクリメントすら決して高速とはいえません。
コンパイラが生成するコードは 1byte 整数型を使用するので、出来ることは減るけど
高速です。

LC-3 は後発なのでさまざまな改良点があります。
その 1つが出力コードの最適化。中でも CPU の内 RAM を変数に割り当てたことで
高速化とともに生成コードの縮小化が出来ました。
もう 1つは用途を考えてゲーム向きの命令を用意したこと。

実際に作ったのはおそらく 1987年頃。1988年に掲載された Kind-1 などの一連の
ゲームも、最初 LC-3 上で作っていたからです。
コンパイラ自体が雑誌に未掲載だったため、投稿前にマシン語で書き直しました。

ネットが無い時代なので何らかの情報入手も雑誌が頼りでした。
プログラムの入手も誌面を埋め尽くすダンプリストを見ながら手打ちです。

投稿も同様、ワープロもなくて原稿用紙に手書きです。
プログラムの説明や入力方法、使い方、メモリマップやワークエリア等を原稿用紙に
手で書いて、カセットテープと一緒に郵送します。
だから 8bit パソコン時代のプログラムはデジタルデータとして手元に残って
いないのです。

もしあるなら web ページとかに掲載できるのですが、プログラムの保存方法は紙に
印字するかカセットテープだけ。
CE-125 は感熱紙なので消えているかもしれないし、仮にテープから読めても
通信機能がないので PC に転送する手段がありません。
手書きの資料はまだ残ってると思いますが、雑誌を探した方が確実な気がしてきました。

雑誌は投稿しても必ず掲載されるわけではありません。
だいたい半数くらいは没で、初期の頃はもっとたくさん。
投稿した原稿のコピーも全く無いので失われているものは結構多いのかもしれません。

便利な環境に慣れすぎたので、原稿用紙に文字数を数えながら清書するなんてたぶん
もう出来ないでしょう。送っても結果が出るまで何ヶ月もかかります。

今なら文章も簡単に書けるし編集可能だし、紙を媒体にする必要もありません。
ネットにはすぐ反映されるし、おそらく一度デジタル化したデータは簡単に失われたり
読めなくなったりすることもないでしょう。複製も容易に取っておけます。
手で打ち込み直す必要もないし配布も入手も簡単です。

そのときは全く考えもしなかったけどいろいろなことが不便でした。
配信、バックアップのための複製、転送、蓄積など。
紙や印刷の登場以前と以後といったら大げさかもしれませんが、コンピュータの
ネットワーク化とデータのデジタル化は、同じように生活を変えたのかもしれません。

Direct2D (7) Tessellate の 2

昨日の Tessellate 結果を図示したものです。

FillGeometry() 相当の場合
ポリゴン化の状態をわかりやすくするために flatteningTolerance = 4 で Tessellate
しています。

direct2d

iFactory->CreateEllipseGeometry(
	D2D1::Ellipse( D2D1::Point2F( 0,0f, 0.0f ), 80.0f, 80.0f ), &iElli );
DummySink	tsink;
iElli->Tessellate( NULL, 4.0f, &tsink );

DrawGeometry() 相当の場合
strokeWidth = 4.0 で描画した場合のポリゴン分割です。
こちらも見やすいように flatteningTolerance = 4 で分割しています。

direct2d

iFactory->CreatePathGeometry( &iPath );
ID2D1GeometrySink*	isink;
iPath->Open( &isink );
iElli->Widen( 10.0f, NULL, NULL, isink );
isink->Close();
DummySink	tsink;
iPath->Tessellate( NULL, 4.0f, &tsink );

デフォルトのままだとこれくらいの分割です。
水平方向の分割かつ Strip になっているのがわかります。

direct2d

テストに使った DummySink の定義。

class DummySink : public ID2D1TessellationSink {
private:
    static ID2D1PathGeometry*	iPath;
    static ID2D1GeometrySink*	iSink;
public:
    DummySink()
    {
    }
public:
    STDMETHOD_( void, AddTriangles )( const D2D1_TRIANGLE* triangles, UINT trianglecount )
    {
	for( UINT i= 0 ; i< trianglecount ; i++, triangles++ ){
	    iSink->BeginFigure( triangles->point1, D2D1_FIGURE_BEGIN_FILLED );
	    iSink->AddLine( triangles->point2 );
	    iSink->AddLine( triangles->point3 );
	    iSink->EndFigure( D2D1_FIGURE_END_CLOSED );
	}
    }

    STDMETHOD_(ULONG, AddRef )( THIS )
    {
	return	0;
    }
    STDMETHOD_(ULONG, Release )( THIS )
    {
	return	0;
    }
    STDMETHOD( QueryInterface )( THIS_ REFIID riid, void** obj )
    {
	return	E_UNEXPECTED;
    }
    STDMETHOD( Close )()
    {
	return	S_OK;
    }
    static void	Create( ID2D1Factory* factory )
    {
	factory->CreatePathGeometry( &iPath );
	iPath->Open( &iSink );
    }
    static void	ClosePath()
    {
	if( iSink ){
	    iSink->Close();
	    iSink= NULL;
	}
    }
};

ID2D1PathGeometry*	DummySink::iPath= NULL;
ID2D1GeometrySink*	DummySink::iSink= NULL;

そのまま DrawGeometry() で path を描画すると、とがった角にトゲが生えたような
描画になることがあります。折れ線のつなぎ処理が入っているようです。

direct2d

描画用に ID2D1StrokeStyle を作成しておくとトゲが無くなります。

関連エントリ
Direct2D (6) Tessellate
Direct2D (5) ID2D1Geometry を使う
Direct2D (4) Direct2D の描画
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換

Direct2D (6) Tessellate

FillMesh() はまだ成功していません。
Tessellate() で座標を取ってみました。

iFactory->CreateRectangleGeometry(
		D2D1::RectF( 0.0f, 0.0f, 10.0f, 10.0f ), &iRect );
DummySink	tsink;
iRect->Tessellate( NULL, &tsink );

まずはただの矩形から。
少々勘違いしていたのは、AddTriangles() はまとめて一度だけ呼ばれるのかと思ったら
1 Triangle 毎に何度も呼び出されること。
結果は次の通り。

t0= 10.000000 0.000000, 10.000000 10.000000,  0.000000 10.000000
t1=  0.000000 0.000000,  0.000000 10.000000, 10.000000  0.000000

頂点の並びが少々おかしい気がします。
それぞれ回転方向が逆になっているので、Strip を展開したような形でしょうか。

ID2D1RectangleGeometry ではなく Path で同様の形を作ってみます。

ID2D1GeometrySink*	isink;
iFactory->CreatePathGeometry( &iPath );
iPath->Open( &isink );
isink->BeginFigure( D2D1::Point2F( 0.0f, 0.0f ), D2D1_FIGURE_BEGIN_FILLED );
	isink->AddLine( D2D1::Point2F( 20.0f,  0.0f ) );
	isink->AddLine( D2D1::Point2F( 20.0f, 20.0f ) );
	isink->AddLine( D2D1::Point2F(  0.0f, 20.0f ) );
	isink->EndFigure( D2D1_FIGURE_END_CLOSED );
	isink->Close();
iPath->Tessellate( NULL, &tsink );

全く同じです。

t0= 20.000000 0.000000, 20.000000 20.000000,  0.000000 20.000000
t1=  0.000000 0.000000,  0.000000 20.000000, 20.000000  0.000000

パスの方向を逆にしても同じでした。

isink->BeginFigure( D2D1::Point2F( 0.0f, 0.0f ), D2D1_FIGURE_BEGIN_FILLED );
isink->AddLine( D2D1::Point2F(  0.0f, 20.0f ) );
isink->AddLine( D2D1::Point2F( 20.0f, 20.0f ) );
isink->AddLine( D2D1::Point2F( 20.0f,  0.0f ) );
isink->EndFigure( D2D1_FIGURE_END_CLOSED );

t0= 20.000000 0.000000, 20.000000 20.000000,  0.000000 20.000000
t1=  0.000000 0.000000,  0.000000 20.000000, 20.000000  0.000000

半径 10 の円の場合。

iFactory->CreateEllipseGeometry(
	D2D1::Ellipse( D2D1::Point2F( 0,0f, 0.0f ), 10.0f, 10.0f ), &iElli );
iElli->Tessellate( NULL, &tsink );

22個の Triangle に分割されています。ちなみに半径 30だと 30個。

t0= -2.015625 9.796875, 0.000000 10.000000, 2.015625 9.796875
t1= 3.890625 9.218750, 2.015625 9.796875, -2.015625 9.796875
t2= -3.890625 9.218750, -2.015625 9.796875, 3.890625 9.218750
t3= 7.062500 7.062500, 3.890625 9.218750, -3.890625 9.218750
t4= -7.062500 7.062500, -3.890625 9.218750, 7.062500 7.062500
t5= 9.218750 3.890625, 7.062500 7.062500, -7.062500 7.062500
t6= -9.218750 3.890625, -7.062500 7.062500, 9.218750 3.890625
t7= 9.796875 2.015625, 9.218750 3.890625, -9.218750 3.890625
t8= -9.796875 2.015625, -9.218750 3.890625, 9.796875 2.015625
t9= 10.000000 0.000000, 9.796875 2.015625, -9.796875 2.015625
t10= -10.000000 0.000000, -9.796875 2.015625, 10.000000 0.000000
t11= 9.796875 -2.015625, 10.000000 0.000000, -10.000000 0.000000
t12= -9.796875 -2.015625, -10.000000 0.000000, 9.796875 -2.015625
t13= 9.218750 -3.890625, 9.796875 -2.015625, -9.796875 -2.015625
t14= -9.218750 -3.890625, -9.796875 -2.015625, 9.218750 -3.890625
t15= 7.062500 -7.062500, 9.218750 -3.890625, -9.218750 -3.890625
t16= -7.062500 -7.062500, -9.218750 -3.890625, 7.062500 -7.062500
t17= 3.890625 -9.218750, 7.062500 -7.062500, -7.062500 -7.062500
t18= -3.890625 -9.218750, -7.062500 -7.062500, 3.890625 -9.218750
t19= 2.015625 -9.796875, 3.890625 -9.218750, -3.890625 -9.218750
t20= -2.015625 -9.796875, -3.890625 -9.218750, 2.015625 -9.796875
t21= 0.000000 -10.000000, -2.015625 -9.796875, 2.015625 -9.796875

円の中心に頂点がありません。水平方向にスライスされているので、やはり Strip に
展開されているのだと思われます。

分割数は Tessellate() 2番目のパラメータで指定出来ます。
  Tessellate( NULL, ここ, &tsink );
省略時は D2D1_DEFAULT_FLATTENING_TOLERANCE が渡されており、これは 0.25f です。

値を大きくすると、5.0 くらいで菱形の 4角形になります。
0.0f を与えると 3862個に分割されました。

# 5.0
t0= -10.000000 0.000000, 0.000000 10.000000, 10.000000 0.000000
t1=  0.000000 -10.000000, -10.000000 0.000000, 10.000000 0.000000

RenderTarget 作成時 D2D1_RENDER_TARGET_PROPERTIES dpiX/dpiY に 0 を与えると
デフォルトの 96dpi になります。
96dpi の時、長さは pixel 相当となり、dpi 値を増やすとピクセル密度が高くなります。
相対的に同じサイズの RenderTarget に描かれる図形は大きくなります。

dpi を考慮した実際に描画される大きさと、Tessellate() 2番目の数値によって
生成される Triangle の数が決められています。

関連エントリ
Direct2D (5) ID2D1Geometry を使う
Direct2D (4) Direct2D の描画
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換

ADTEC AD-MP15A 小型プロジェクター

ADTEC の小型プロジェクター AD-MP15A を手に入れました。

ADTEC AD-MP15Aシリーズ Bit LED Projector

わずか 15ルーメンで解像度は VGA 640×480。
ビデオ入力だけでなく VGA 端子もあるので PC 画面も映すことが出来ます。
VGA 入力信号は 640×480 または 800×600 に対応。

周りが暗く、かつ近い距離だと思ったよりもきれいに出ます。
ピントさえ合えば画像はくっきり。
30cm の距離で実測 8.2inch くらい。
夜明かり無しの暗闇だったら 1.6m で 39inch 程度。一応みれました。

AD-MP15A

↑PS3 の画面をビデオ入力で。背景がゆがんでるのはスクリーンがただの封筒だから。

周囲の明るさにかなり依存します。
黒い領域=スクリーンの色 となるわけですが、普段スクリーンが白に見える部屋で
あればそれはそのまま白です。
投影映像にはそれを打ち消して、スクリーンの色が暗く見えるだけの明るさが
必要となります。そのためには

・周囲を暗くする
・プロジェクタをスクリーンに近づける

のどちらかです。
また映像ソースにも依存するので、明るいシーンだと大丈夫でも背景が真っ黒な
暗いシーンになると見にくくなります。

・夜や暗い部屋で使用する
・バッテリー駆動で持ち歩けて VGA 入力のある超小型モニタとして
・スクリーンに非常に近づけて小さい画面を一人で見る
・比較的明るい画面が中心

といったケースが合っているかもしれません。

フォーカス調整可能な範囲が決まっています。
試したところ、だいたい 16cm~160cm くらいでした。

16cm = 4.5inch
30cm = 8.3inch
58cm = 15inch
95cm = 22inch
160cm = 39inch

小さいけれどそれなりに熱を持つのかファンが入っているようです。
音はわずかに聞こえる程度でほとんど気になりません。

本体サイズは非常に小さくバッテリーも内蔵して 147g。
バッテリー駆動時間は 40分。

AD-MP15A
↑側面にコネクタ類あり

右の赤いのはたまたま近くにあったサイズ比較用(?)のビジュアルメモリ(Dreamcast)で、
付属品ではないです。

コネクタやスイッチは全部側面にあります。裏はバッテリーカバー。
映像入力コネクタは独自なので専用のケーブルも必要となります。
ケーブルはどれも非常に細くできており取り回ししやすくなっています。

AD-MP15A
↑付属のケーブルと専用コネクタ

パッケージには三脚も同梱されています。
全体的に軽いので、ケーブルに引っ張られるとすぐに動いてしまうのは仕方ないところ。

スピーカーも内蔵されておりヘッドホン端子もありますがボリューム調整は出来ません。

個人的には気に入りました。
小さいし使いどころが限られてくるけど、今までとは違った活用方法が考えられます。
プロジェクターといえばこう使う、といった先入観さえ無ければ非常におもしろいと思います。
今後は同様の小型プロジェクタが登場して結構早いペースで進化するのではないでしょうか。

UQ WiMAX 申し込み

すでにモニターテストの受付が始まっていますが、エリア外の人も直接電話で
申し込めば端末を購入することができます。
ユーザーサポートセンターに問い合わせるも混雑してるようでなかなかつながらず、
何度か待たされつつ申込書の送付を頼むことが出来ました。

UQ WiMAX

気になっていたのはこの部分。モニター募集のところに

>発送: 2009年2月26日(木)~2009年3月中旬にかけてデータ通信カードを順次
>発送いたします。

発送が 3月中旬までかかると書いてあります。

念のためきいてみると、購入者には優先して発送するので大丈夫だそうです。
ただしサービス開始の 2/26 必着にはならず、26 日以降に発送するため数日は
余裕を見てくださいとのことでした。