発表してきた.学生奨励賞をもらったおかげで,ただ酒と夕食と1万円分の図書カードゲット.
眠かったので帰ってからすぐ寝た.
http://www.rubyist.net/~matz/20070918.html#p03
http://shinh.skr.jp/m/?date=20070928#p01
何日か前にgdbの入出力をごまかして作ったのと,そんなに変わらない気がする.
[TOTORO ~] > ic
ic> char* v="hoge";
A syntax error in expression, near `v="hoge";'.
ic> puts(v);
No symbol "v" in current context.
ic> int x = printf("Hello\n");
A syntax error in expression, near `x = printf("Hello\n");'.
ic> printf("%d\n", x);
No symbol "x" in current context.
ic>
負けてる? ^^; gdbが普通に使える人が触れば
ic> "hoge"
$1 = "hoge"
ic> puts($1)
hoge
$2 = 10
ic> printf("Hello\n")
Hello
$3 = 6
ic> printf("%d\n", $3)
6
$4 = 2
ic> 3*2
$5 = 6
ic> 5<<3
$6 = 40
ic>
勝ってる気がする.
バイナリハックかくあるべし、という感じ。
全然ばいなりーじゃないですが^^;
バグ修正.なんだか大変な事になってたので,最初に取った方法に戻しました.
XBYAKでcallに関数ポインタを直接入れれるようになったので,その辺はいじってあります.
あと,相対ジャンプ等の関係(?)で微妙に修正を入れたりしました.
http://homepage1.nifty.com/herumi/diary/0709.html#25
call((int)func);
たまにおかしな動きをするのですが,私が悪いような気がしなくもないので
今度調べてみます.今日は今から飲むので・・・
void myputs(const char*s)
{
puts(s);
}
struct Hello : public Xbyak::CodeGenerator
{
Hello()
{
push((int)"hello");
call((int)puts);
pop(eax);
}
};
struct Hello2 : public Xbyak::CodeGenerator
{
Hello2()
{
push((int)"hello");
call((int)myputs);
pop(eax);
}
};
int main(int argc, char**argv)
{
Hello h;
Hello2 h2;
((f*)h.getCode())(); // OK
((f*)h2.getCode())(); // NG
return 0;
}
だった.
UMのJITにひどいバグがあるなぁ・・・ 前のコードに戻そうかなぁ
メモリのコピーを減らそうとして,スタックを食い尽くすとか,ひどい改悪をしたんだなぁ
だいぶ前に修正したものをアップしておきました.
どんな問題点があったか覚えてないので,かなり怪しいですが・・・
http://www.blokus.com/online-game/
と戦ってみたら,勝った.4文字コードを打つのがめんどくさい.
しょぼい処理系を書くのに飽きたので,AI作り.
とりあえず2種類AIを書いてみた.これで対戦が見れる.
片方はランダムですが・・・
致命的に恥ずかしいものになる^^;
2時間半の睡眠で28時間起きてると,簡単には起きないようです.
寝ている間に妖精さんがsandmarkを54秒で実行できるようにしてくれました.
[TOTORO d/C++Projects/umjit] > time Release/umjit sandmark.umz trying to Allocate array of size 0.. trying to Abandon size 0 allocation.. 省略 0. a8d1619e.5540e6cf SANDmark complete. 0.015u 0.000s 0:54.43 0.0% 0+0k 0+0io 690pf+0w [TOTORO d/C++Projects/umjit] >
ICFPC2006
http://www.boundvariable.org/
XBYAK
http://homepage1.nifty.com/herumi/soft/xbyak.html
ジャストタイムコンパイラ方式
http://ja.wikipedia.org/wiki/%E3%82%B8%E3%83%A3%E3%82%B9%E3%83%88%E3%82%A4%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E6%96%B9%E5%BC%8F
よく分からない内にsandmarkの実行が1分切れていたのでソース公開します.
_msizeやインラインアセンブラを使っているので,VCでしかコンパイルできません.
gccでコンパイルするのに必要な作業は,そんなに多くは無いと思います.
前半で定義されている,C++でまんま書かれた関数達は,コンパイルを283行目のdefaultに任せると使われます.
普段は使っていませんが,ArrayIndexとArrayAmendmentのコンパイルは遅いので
そっちに任せると少し幸せです.
#include <vector>
#include <iostream>
#include <fstream>
#include "xbyak.h"
using namespace std;
typedef unsigned int platter;
vector<platter> array0;
platter*pArray0 = NULL;
platter registers[8]={0};
void (*compiled)() = NULL;
//const unsigned int codeAlign=23;
vector<int> labels;
unsigned char*loadProgramCode;
void compile();
void condMove(int a, int b, int c)
{
if(registers[c])
registers[a]=registers[b];
}
void arrayIndex(int a, int b, int c)
{
if(registers[b]==0)
registers[a]=array0[registers[c]];
else
registers[a]=((platter*)registers[b])[registers[c]];
}
void arrayAmendment(int a, int b, int c)
{
if(registers[a]==0)
array0[registers[b]]=registers[c];
else
((platter*)registers[a])[registers[b]]=registers[c];
}
void addition(int a, int b, int c)
{
registers[a]=registers[b]+registers[c];
}
void multiplication(int a, int b, int c)
{
registers[a]=registers[b]*registers[c];
}
void division(int a, int b, int c)
{
registers[a]=registers[b]/registers[c];
}
void nand(int a, int b, int c)
{
registers[a]=~(registers[b]®isters[c]);
}
void halt(int a, int b, int c)
{
exit(0);
}
void alloc(int a, int b, int c)
{
registers[b]=(platter)calloc(registers[c], sizeof(platter));
}
void abandonment(int a, int b, int c)
{
free((void*)registers[c]);
}
void output(int a, int b, int c)
{
putchar(registers[c]);
fflush(stdout);
}
void input(int a, int b, int c)
{
registers[c]=getchar();
}
int loadProgram(int a, int b, int c)
{
if(registers[b]!=0)
{
int size=_msize((platter*)registers[b]);
array0.resize(size/sizeof(platter));
memcpy(&array0[0], (void*)registers[b], size);
pArray0=&array0[0];
compile();
}
return labels[registers[c]]; // ジャンプ先を返す
}
void move(unsigned int platter)
{
int a = (platter>>25)&0x7;
int value = platter&0x1FFFFFF;
registers[a]=value;
}
typedef void func(int, int, int);
func*funcs[]={
condMove,
arrayIndex,
arrayAmendment,
addition,
multiplication,
division,
nand,
halt,
alloc,
abandonment,
output,
input,
};
struct jit : public Xbyak::CodeGenerator
{
union
{
unsigned int cnt;
char buf[4];
}label_;
void nextLabel()
{
++label_.cnt;
int i;
for(i=0; i<4; i++)
{
if(label_.buf[i]!=0)
break;
label_.buf[i]=1;
}
}
void stdOpe(unsigned int ope, unsigned int a, unsigned int b, unsigned int c)
{
/*
default: でC++で書いた関数を呼ぶコードを生成しているので
case 何か: のブロックをコメントアウトしても動きます.
default以外のcaseを消すと,とても消極的にコンパイルされたコードが生成されます
*/
switch(ope)
{
case 0: // cond mov
{
if(a!=b)
{
nextLabel();
mov(eax, ptr[registers+c]);
test(eax, eax);
je(label_.buf);
mov(eax, ptr[registers+b]);
mov(ptr[registers+a], eax);
L(label_.buf);
}
break;
}
/* case 1からcase 2までをコメントアウトすると
// コンパイルにかかる時間がとても短くなりますが
// 微妙に実行速度が遅くなります. sandmark : 54.43sec -> 69.06sec (CoreDuo 2.0Ghz)
//
// umixはさくさく起動した方が嬉しいのでコメントアウトしておきます.
case 1: // array index
{
nextLabel();
mov(eax, ptr[registers+b]);
test(eax, eax);
jnz(label_.buf);
// array0へのアクセス
lea(eax, ptr[pArray0]);
L(label_.buf);
mov(ecx, ptr[registers+c]);
mov(eax, ptr[eax + ecx*4]);
mov(ptr[registers+a], eax);
break;
}
case 2: // array amendment
{
nextLabel();
mov(eax, ptr[registers+a]);
test(eax, eax);
jnz(label_.buf);
// array0へのアクセス
lea(eax, ptr[pArray0]);
L(label_.buf);
mov(ecx, ptr[registers+b]);
mov(edx, ptr[registers+c]);
mov(ptr[eax+ecx*4], edx);
break;
}
//*/
case 3: // add
{
mov(eax, ptr[registers+b]);
add(eax, ptr[registers+c]);
mov(ptr[registers+a], eax);
//registers[a]=registers[b]+registers[c];
break;
}
case 4: // mult
{
mov(eax, ptr[registers+b]);
mov(ecx, ptr[registers+c]);
mul(ecx);
mov(ptr[registers+a], eax);
break;
}
case 5: // div
{
xor(edx, edx);
mov(eax, ptr[registers+b]);
mov(ecx, ptr[registers+c]);
div(ecx);
mov(ptr[registers+a], eax);
break;
}
case 6: // nand
{
mov(eax, ptr[registers+b]);
and(eax, ptr[registers+c]);
not(eax);
mov(ptr[registers+a], eax);
break;
}
case 7: // halt
{
push(0);
lea(eax, ptr[exit]);
call(eax);
break;
}
case 8: //alloc
{
push(4);
push(ptr[registers+c]);
lea(eax, ptr[calloc]);
call(eax);
mov(ptr[registers+b], eax);
pop(eax);
pop(eax);
break;
}
case 9: // abandonment
{
push(ptr[registers+c]);
lea(eax, ptr[free]);
call(eax);
pop(eax);
break;
}
case 10: // output
{
push(ptr[registers+c]);
lea(eax, ptr[putchar]);
call(eax);
push((unsigned int)stdout);
lea(eax, ptr[fflush]);
call(eax);
pop(eax);
pop(eax);
break;
}
case 11: // input
{
lea(eax, ptr[getchar]);
call(eax);
mov(ptr[registers+c], eax);
break;
}
case 12: // load program
{
// LoadProgramの中で,array0をコピーしてしまうと
// compiledの中身が破壊されるので,これだけ別領域(loadProgramCode)に作る必要がある.
// 呼び出し規約はstdcall
push(c);
push(b);
push(a);
jmp(ptr[&loadProgramCode]); // あらかじめ作っておいたものを呼ぶ
break;
}
default:
{
push(c);
push(b);
push(a);
call(ptr[funcs+ope]);
add(esp, 12);
}
}
}
jit(int codesize)
: CodeGenerator(codesize)
{
label_.cnt=0;
}
void gen(int bits)
{
unsigned int ope=(bits>>28)&0xF;
if(ope<13)
{
unsigned int c = bits &0x7; // 000000111
bits=bits>>3;
unsigned int b = bits &0x7; // 000000111
bits=bits>>3;
unsigned int a = bits &0x7; // 000000111
stdOpe(ope, a, b, c);
}
else if(ope==13)
{
unsigned int a = (bits>>25)&0x7;
unsigned int value = bits&0x1FFFFFF;
mov(ptr[registers+a], value);
}
}
};
// LoadProgramの中で,array0をコピーしてしまうと
// compiledの中身が破壊されるので,これだけ別領域に作る必要がある.
// 呼び出し規約はstdcallのつもりで呼べばOK
struct GenLoadProgram : public Xbyak::CodeGenerator
{
GenLoadProgram()
{
lea(eax, ptr[loadProgram]);
call(eax); // ジャンプ先のアドレスを返す
pop(edx);
pop(edx);
pop(edx);
// 実行指の移動
add(eax, ptr[&compiled]);
jmp(eax);
}
};
void compile()
{
jit jitc(array0.size()*29); // 生成されるコードは1platterあたり最大で29バイト(今のところ・・・)
pArray0=&array0[0];
labels.resize(array0.size());
// unsigned int max=0, last=0;
try
{
int i=0;
vector<unsigned int>::iterator it;
for(it=array0.begin(); it!=array0.end(); ++it,++i)
{
labels[i]=jitc.getSize(); // array0[i]のコードが,x86コードで何バイト目なのか分からないと,jmpとかできない
jitc.gen(*it);
// if(labels[i]-last > max)
// max=labels[i]-last;
// last=labels[i];
}
// cout << "largest code size: " << max << endl;
}
catch (Xbyak::Error err)
{
printf("ERR:%s(%d)\n", Xbyak::ConvertErrorToString(err), err);
}
catch (...)
{
printf("unkwon error\n");
}
compiled=(void (*)())jitc.getCode();
compiled();
}
inline unsigned int _fastcall changeEndian(unsigned int v)
{
_asm
{
bswap ecx;
mov eax, ecx;
}
}
int main(int argc, char**argv)
{
ifstream ifs;
char*progName="umix.umz";
if(argc==2)
progName=argv[1];
ifs.open(progName, ios::binary);
if(!ifs.good())
{
cerr << "ERROR : File '"<< progName << "' was not found." << endl;
return 1;
}
const int BS=0x400;
unsigned int tmp[BS];
for(;!ifs.eof();)
{
ifs.read((char*)tmp, sizeof(platter)*BS);
int i, cnt = ifs.gcount()/sizeof(platter);
for(i=0; i<cnt; i++)
array0.push_back(changeEndian(tmp[i]));
}
ifs.close();
GenLoadProgram genLP;
int loadProgramSize=genLP.getSize();
loadProgramCode = (unsigned char*)malloc(loadProgramSize);
memcpy(loadProgramCode, genLP.getCode(), loadProgramSize);
compile();
return 2;
}
XBYAK面白かった.オペコードを知らなくてもコンパイラが書けるなんて,とても素晴らしいです.
ただ,callに関数ポインタを直接渡せないのが不便でした.
KETTLE BRAND CHIPSの「SEA SALT & VINEGAR」を買った
http://www.kettlefoods.com/index.php?cID=36
袋を開けると,すぐにすっぱいにおいが・・・ 強烈な味ですが,少し癖になりそうな気が・・・.多分もう買いませんが^^;
[TOTORO C++Projects/umjit/Release] >time ./umjit ../codex.umz > /dev/null << EOF \b.b? (\b.bb)(\v.vv)06FHPVboundvarHRAk ? x ? EOF 0.015u 0.015s 0:08.60 0.2% 0+0k 0+0io 668pf+0w [TOTORO C++Projects/umjit/Release] >
夏のプロシンの時に聞いた話だと,4秒とかでいけるらしい.きついなぁ
今日は3時に目が覚めました・・・.寝なおせないのが問題です.
[TOTORO C++Projects/umjit/Release] >time ./umjit ../codex.umz << EOF ? (\b.bb)(\v.vv)06FHPVboundvarHRAk ? x ? EOF compile ok self-check succeeded! enter decryption key: decrypting... ok LOADING: 9876543210 == CBV ARCHIVE == VOLUME ID 9 Choose a command: p) dump UM data x) exit ? ? See you soon! 0.015u 0.015s 0:11.46 0.1% 0+0k 0+0io 670pf+0w [TOTORO C++Projects/umjit/Release] >
ぉぉ すごく早くなった.
で,JITコンパイルしていない古いUMと比較してみたら,古い方は30秒ちょっとだった.
昨日の50秒のやつは,JIT無しより遅かったのか・・・
[TOTORO C++Projects/umjit/Release] >time ./umjit.exe ../sandmark.umz > /dev/null 0.015u 0.000s 1:11.87 0.0% 0+0k 0+0io 673pf+0w [TOTORO C++Projects/umjit/Release] >
コンパイルが遅いのも問題だ(sandmarkなら一瞬ですが,umixだと遅い)
アルコールが抜けきらない気がします・・・
JITアセンブラXBYAK
http://homepage1.nifty.com/herumi/soft/xbyak.html
面白そうなので,使い慣れておきたかったのですが,使いたくなるようなコードを書くことも無く・・・でした.
が,最近布教のおかげかICFPC2006のUMを実装する人がリアルの周りで増えてきたので,UMのJITコンパイラを書いてみることにしました.
昨日の午後がんばったのですが,中途半端に動く状態でバグが取れないまま眠気に負けてました.
今日は,弱気なコードで再挑戦.Cな関数とJITコンパイルしたコードを行き来する感じの実装.
XBYAKにも慣れたので,1時間ちょっとで微妙に動く状態には持っていけた.
ただ,やっぱり同じようなバグで止まる.逃げの一手で書いたコードが動かないって事は...と冷静になって考えてると
LoadProgram -> JITコンパイル -> free(古いコード) -> return to古いコード -> アクセス違反
な,事になってる事に気づいた.今日は眠くなかったのが素晴らしい.
現時点でsandmarkが私のヘタレなC++の実装より少し早いくらい.
まだかなりチューニングできそうなので,良い感じです.
ただ,夏のプロシンで聞いたluajitで実装したUMのスピードが私には出せる気がしなくてほげー
とりあえず
SANDmark complete.
のメッセージをコンソール上ではじめてみました^^;
あと,#2. Array Amendment.でArray0に書き込みをした部分のコード生成を省略してもsandmarkが動いてびっくりしたとかなんとか.
自己書き換えっぽい事はsandmarkやumixの起動ではやってないみたいです.(全体では分かりません)
自己書き換え前提のJITコンパイラを書いたので,この辺を無視していいなら,だいぶ高速化できる気がします...
ほぼ全体をJITコンパイルしても,思ったより早くない^^; sandmarkの実行が3倍ちょっと早いくらい・・・
最適化しないと駄目かも.
明日は早く起きなきゃいけないはずなんだけど,,,
一応実行速度(core duo 2.0GHz Windows XP)
[TOTORO C++Projects/umjit/Release] >time ./umjit.exe ../sandmark.umz > /dev/null 0.031u 0.000s 1:21.96 0.0% 0+0k 0+0io 671pf+0w [TOTORO C++Projects/umjit/Release] >
luajitのumまであと20秒.
まだ高速化の余地は大量にあるので,なんとかなるかな?(こっちはC++だしなぁ)
明日は,自己書き換えを無視して,スピード勝負をしてみよう.
[TOTORO C++Projects/umjit/Release] >time ./umjit.exe ../codex.umz << EOF ? (\b.bb)(\v.vv)06FHPVboundvarHRAk ? x ? EOF largest code size: 24 self-check succeeded! enter decryption key: decrypting... ok LOADING: 9876543210 == CBV ARCHIVE == VOLUME ID 9 Choose a command: p) dump UM data x) exit ? ? See you soon! 0.031u 0.000s 0:50.32 0.0% 0+0k 0+0io 695pf+0w [TOTORO C++Projects/umjit/Release] >
codexは1分切れた.
早起きの次はやっぱり寝すぎで,16時位に活動を始めました・・・
Meshiが先週参加していたe-learning関係の学会の話を少し聞いた.
C言語インタプリタとかがあると喜ばれるらしいです.軽く探してましたが,良い感じの物はありませんでした.
適当なものならgdbで簡単に作れそうだったので,やってみました.
ただのgdbラッパですが,気持ちの良い動作をさせるのに手間取りました・・・
こんな感じに使えます.
[TOTORO /d/C++Projects/ic] > ./ic.sh
ic> printf("%02X\n", 'A')
41
$1 = 3
ic> puts("hogehoge")
hogehoge
$2 = 10
ic> 3+4
$3 = 7
ic> cos(3.14)
$4 = 639
ic> /f cos(3.14)
$5 = 8.95429719e-43
[TOTORO /d/C++Projects/ic]
浮動小数点だめじゃないか,,,
以下ソース(ライセンスはNYSL)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <signal.h>
void b(){}
int main()
{
setvbuf(stdout, 0, _IONBF, 0);
b();
return 0;
}
#!/usr/bin/perl
$|=1;
while(<>)
{
chomp;
if("" eq $_){ next }
elsif(substr("quit", 0, length($_)) eq $_)
{
print "quit";
exit 0;
}
else
{
print "p $_\n";
}
}
#!/usr/bin/perl
$|=1;
for($i=0;$i<4;$i++){<STDIN>}
while(!eof(STDIN))
{
$buf.=getc STDIN;
if(substr("(gdb)", 0, length($buf)) ne $buf)
{
print $buf;
$buf ="";
}
elsif($buf eq "(gdb)")
{
print "ic>";
$buf ="";
}
}
br b run
#!/usr/bin/sh perl in.pl | gdb -q -x x.txt a | perl out.pl
gcc -g -lm ic.c
のようにコンパイルして
./ic.sh
のように使います.a.outが嫌な場合は,コンパイルのオプションとic.shのaの部分を変えればOKです.
適当すぎですが笑わないでください.
パイプを使うとかっこよくみえるかもしれませんが,めんどくさいのでこのままです.
あると意外に便利な気がします.
昨日は18時過ぎに目が覚めて,26時過ぎに寝た.
今日は6時前に目が覚めた.
先週買ったLindtのチョコ3枚を食べ終わった.かなり美味しかったのでまた買おう.
いまならのもとで70%OFFですよっ
http://www.videolan.org/vlc/
VLCで動画を再生していると,なぜか他のアプリケーションの,背景色や文字色がが(0,0,1)の部分が動画で上書きされる.
MeadowやPuTTYの背景色を#000001にすると,動画をエディタの背景で再生しながらプログラミングができて素敵.
software/WindowsManagerの最新版を公開しました.
(winapi "Sleep" 1000)のようにWindowsAPIを呼び出せるようになりました.ただし,全ての引数はlongとして扱われます.
http://d.hatena.ne.jp/shi3z/20070911/1189493767
後輩がなんか気にしてた.気になるなら勉強すればいいのに,何もしないからほげほげ.
私はアセンブリとか超苦手なので,必要最低限しか使いません... たまに使うのは仕方が無い時くらいです^^;
先週はWindowsAPIをWindowsManagerから使うために(そういえば公開してない)
while(!apiArgs.empty())
{
long v=*apiArgs.begin();
apiArgs.pop_front();
_asm{
push v;
}
}
long retval;
_asm{
call proc;
mov retval, eax;
mov c1, esp;
}
なコードを書いた.STLとインラインアセンブラとか変な組み合わせだよなぁ...
もし,アセンブリを使うしかない雰囲気を感じたら,アセンブリを覚えるきっかけにすると良いかもしれません.
そんな事より,未女子日女(なぜか一発で変換できる)の教えが大切 > 後輩君
ぷりんてぃん熱が再燃してて大変だ
ffmpegをcygwinでコンパイル.llrintがないと怒られたので適当に直す.
眠たいので,動作確認は寝てから...
http://www.takamagahara.com/printin/top/top01.html#wkly_ptn_070906
未女子日女さまがニコニコデビューしてたなんて!
朝の時点で運休.FITが終わる頃には戻ってるかな・・・?
最初から最後まで参加.台風が着てるので傘を買ったのですがほとんど使いませんでした.うーん.
論文集の目次を見ていると,『「PvsNP」問題の解決:最終章』とかいうタイトルを発見.
講演者はやはり,山口人生さん.これは発表を聞くしかない! とか思ったら,発表は1時間ほど前に終わっていた^^;
午後の最初はMeshiへのおみやげに,C言語の課題自動採点ほげほげな話を聞きに言ったら,やっている事はUMIXの採点システムと似てる感じだった.
めぼしいものを求めて会場中をふらふらして,最後に紙をやぶくシミュレーションと旗のシミュレーションの話を聞きに行った.
旗の話を聞き終わった後にコメントをしたら,後ろの方で話を聞いていた,早大の人(教授?)が
意見を言いたいなら,論文書け.みたいな事を言っていた.何故?w とりあえず,文章を書くのは疲れると思います・・・
コードが1番♪ コメント2番♪ 論文書くのは卒業用♪ (カステラの感じで)
もちろん(?)software/WindowsManager用.
昼から,午後のセッションが始まるまでの間に,実装してみる事にした.
GetProcAddressとインラインアセンブラで適当に書いたら,あっさり動きました.
wmscm> (winapi "Sleep" 1000) 0 wmscm> (winapi "ShowWindow" (get-foreground-window) sw-maximize) 16 wmscm> (winapi "ShowWindow" 1) WARNING: You should call 'ShowWindow' with 2 arguments. 0 wmscm> (winapi "ShowWindow" 1 2 3) WARNING: You should call 'ShowWindow' with 2 arguments. 0 wmscm>
まだ引数も返り値もlongしか使えませんが,自由度が大幅にアップです.
構造体やポインタへの対応も,やれない事はないのですが,必要だとあまり感じていません^^;
文字列くらいは対応しようかなと思っています.
DLLからだけでなく,libcの関数も使えるようにすれば,mallocとかmemsetとか色々使えて嬉しい気がします.(Scheme!?)
静的リンクな関数のアドレスの取得の仕方は知らないので,調べないといけないけれど,,,
ふらふらしたり,気になる所を覗いたり
の乳児と遊んできた.非常にかわいかった.
英字キーボードを使う理由って何なんだろう・・・ と前から思っていたのですが
http://d.hatena.ne.jp/ytqwerty/20060408#p1
英語キーボードですと、言語設計者の想定した記号の打ち方ができるので大変良いのです。
ちょっと英字キーボードがほしくなった
http://moesuku.ofg.jp/
http://www.academianetwork.co.jp/service/moe/
萌えるSquakeの通販がはじまったようです.
さらになんと,MacOS X, FreeBSD, Linuxで動作させる事もできるそうです.
http://d.hatena.ne.jp/ranha/20070830
が,うちの研究室に来ていたらしい.私は家で寝ていました^^;
Meshiだけ話を聞けたそうです.いいないいなー
GoogleとかJaxaとかIBMとか素敵