キーボード派のWindows開発環境

そういえば、こういうサイトに定番っぽい開発環境を書いたのがないので、自己紹介?も兼ねて晒してみます。

開発はだいたいWindows上のvmware(Debian)でやってます。ただし、そこは定番どころばっかりなのでパス。まぁvim+screen+zshです。puttyでつないでますよ。sambaでマウントしてますよ。ええ。それぞれ結構設定しまくっていて、グローバルなSVNサーバーにおいてあります。

Windowsでの開発環境

あんまりWindowsは好きではないけど、Macよりは好きだったりする。というわけでWindows。普段使いのOSでもあるので、主に自分用の小物を書くことが多いです。怠け者なので、自動化できるものはすぐプログラムに置き換えちゃいます。でも言語は結構様々。もとから言語にこだわらず、一番適しているものを使う主義なので小物が多いにもかかわらず、言語は結構多いのかも。

今のところ、だいたい

  • それなりのGUIが必要なアプリ:Delphi6
  • 一枚ウインドウがあるくらいのGUIアプリ:Python(wxWidgets)
  • GUIがなく、立ち上がりの速さや軽さが欲しいもの:C(MinGW)
  • それ以外の小物:Python

という感じで適材適所。

Linuxではまったく定番な感じ(定番が自分にしっくりきた)だったけど、Windowsにはキーボード派の定番ってのは少ない気がするので、結構独特なのかも。方針は

  • キーボードで操作しやすいように。
  • なるべくlinuxと同じような感覚(←これ重要)で。

感覚なので一緒じゃなくてもいいのです。

gvim

開発はほとんどこいつです。Delphi以外は。Linuxのvimと同じ設定ファイルを共有してます。俺はvimがなくちゃ生きていけない人間なので、USBメモリにいれてもち運べるようにしてあります。

Firefox

ブラウザはこいつ。ただしPortableFirefoxです。これもUSBメモリにいれて持ち運べるように。学校にいってもUSBメモリをさすだけで普段の環境。拡張も結構つっこんでます。

cltc

タスク切り替えが便利になるソフト。こんな感じで表示されます。インクリメンタル検索での選択や、カーソルキーでの選択ができます。

windowsでタスク切り替えというとAlt+TABですが、これ非常に使いづらい。そこでこのソフト。ここでキモになるのがキー設定で

  • Ctrl+Shift+zで起動
  • Ctrl+jCtrl+kでタスクを選択

という風に設定しています。俺と同じLinux開発環境の人は分かるでしょう(笑

Ctrl+zがscreenのエスケープでjkはvim。これだけでかなりLinuxと同じ感覚でタスクが選べるようになります。

MigemizeExplorer

こりゃ定番。説明不要ですよねえ。超便利。

craftlaunch

これがないと始まらない。コマンド型ランチャー。craftlanuchラブ。こいつはデスクトップ用と持ち運び用(USBメモリに入れる用)の2個も用意してます。起動用ホットキーはeclipseなんてもんは使ってないのでCtrl+Spaceです。

俺の場合はほぼ全ての作業の起点がこのソフト。ランチャーとしてのソフト起動から簡易シェルとしてまで大活躍。中でもオススメなのはexplorer.exe(windows標準のシェル)+craftlaunchの連携。

craftlaunchというと「あふ」との連携が有名ですが、俺はあえてexplorer.exeと連携してます。というのも、なにぶん普段使いのOSですから、マルチメディアのファイルなんかも多いわけです。重くてもプレビューが見れたりするのは便利なもんです。日本語のファイル名だし、ファイラーつかってもファイル選択するのがめんどくさいもんです。

どうやって連携してるかというと、基本的に小物アプリ+ショートカットキー。この使い方をはじめるきっかけになったのは「せっかくcraftlaunchからキーボードだけでフォルダ開けるんだから、ホームポジションで楽にキーボードだけで閉じたいなあ」という思い。

ctrl+space, ctrl+[

の2ステップでウインドウが閉じられます。ctrlは押しっぱなしでいいので非常に軽快に閉じられます。鉄の小指を持つemacs使いの人ならなおのことでしょう(笑

その他の連携

・フォルダの新規作成 
windowのexplorerの不満はフォルダを作るのがめんどくさいことですよね。キーボードならAlt+F W Fでしょうか。というわけでfiniのような自作コマンドで対応してます。超テキトーなのですが、あげておきます。

amkdir.zip

こいつをctrl+nにショートカットとして割り当ててあります。すると・・・

craftlaunchでフォルダを開く→MigemizeExplorerで快適にフォルダをたどる→「フォルダつくりてえ」→ctrl+space フォルダ名入力 ctrl+n

という感じになるわけで、結構ハッピーです。

・コンソール 
おなじような感じで、表示されているフォルダをカレントディレクトリとしてcmd.exeを起動するものもつくってあります。ほとんどつかわないけど。


以上のような、変なWindowsで日々暮らしています。cltcでブラウザやvim、コンソールを行き来しながらIDEはあんまり使わないでガリガリ書いています。

まぁ、だいたいvmware上のLinuxにいるんですけど(笑

まとめると

  • cltcでctrl+zctrl+j,k。快適ですよ。
  • MigemizeExplorerはWindows標準装備になればいいのに。
  • craftlaunchはあふと連携してもいいけど、explorerと連携してもハッピーですよ。

という感じです。

エミュレーターのほうはあのあとCPUを若干チューニングして、1フレーム0.1はキリました。今はPPUを書いてるんですが、そろそろ就職に備えて家を探さないといけません。京都と関東を行き来するのはいろいろこたえます。移動中はシグマリオンのPocketSchemeでSchemeでも書いて暇つぶしです。

07.27.08/12am

PythonによるNESエミュレータ開発2

実は既に結構挫折気味。

やっぱりPythonではちょっと厳しいかもしれない。

とりあえず一番ややこしいPPU周りの情報を調べて、ちょろちょろ書き始めたあたりでいったんCPU部分のパフォーマンスを調べてみました。

かなり厳しいものがあります。CPU部分のコードはPython的な書き方で書いてたんですが、これじゃ話にならない。まず、アクセサなんてものはつかっちゃいけないのだ。

以下環境はOS:WinXP,CPU:Athlon64 3000+,Memory:1G,Python2.4です。

CPUのレジスタ関連の実装

NESのCPUである6502のレジスタはPCは16bit、それ以外は8bit。8bitの値なんてものはCならunsigned charで一発なんだけど、Pythonにはそんなものない。足し算したらどんどん大きくなるし、引き算したらどんどん小さくなる。ので

python code
  1. class Py6502(object):
  2.   def __init__(self):
  3.     self.A = 0
  4.     self.X = 0
  5.     self.Y = 0
  6.     self.PC = 0
  7.     # .
  8.     # .
  9.     # .
  10.  

見たいなクラスにするとして、足し算するときなどは必ずself.A = (self.A + x) & 0xffみたいにして8bitに収めないといけない。ここで

python code
  1. def _create_n_bit_property(name, mask):
  2.   rname = "_"+name
  3.   result = {
  4.     name:property(lambda self: getattr(self, rname),
  5.                   lambda self,v : setattr(self, rname, v & mask),
  6.                   )
  7.   }
  8.   return result
  9.  
  10. class ForceNbitType(type):
  11.   def __new__(cls, class_name, class_bases, classdict):
  12.     names = classdict.get("__16bit__")
  13.     for name in names:
  14.       classdict.update(_create_n_bit_property(name, 0xffff))
  15.     names = classdict.get("__8bit__")
  16.     for name in names:
  17.       classdict.update(_create_n_bit_property(name, 0xff))
  18.     cls = type.__new__(cls, class_name, class_bases, classdict)
  19.     return cls
  20.  

のようなmetaclassをつくって

python code
  1. class Py6502(object):
  2.   __metaclass__ = ForceNbitType
  3.   __8bit__ : "A", "X", "Y"
  4.   __16bit__ : "PC",
  5.   def __init__(self):
  6.     self._A = 0
  7.     self._X = 0
  8.     self._Y = 0
  9.     self._PC = 0
  10.     # .
  11.     # .
  12.     # .
  13.  

とすればself.A += 10とかしてもちゃんと8bitに収まる。

非常に上手くかけるんですが、こんなのやってらんない。遅すぎる。1Frame分(28000サイクル程度)の実行に0.5秒(笑

getattr

getattrは遅い。getattrの真価は第3引数でdefault値が指定できること(だと思う)。今回のように確実に属性が存在することが分かっているならself.__getattribute__(name)を使うと結構違ってくる。ちなみにPythonのソースではこんな感じ。

c code
  1. static PyObject *
  2. slot_tp_getattro(PyObject *self, PyObject *name)
  3. {
  4.     static PyObject *getattribute_str = NULL;
  5.     return call_method(self, "__getattribute__", &getattribute_str,
  6.                "(O)", name);
  7. }
  8.  
  9. static PyObject *
  10. slot_tp_getattr_hook(PyObject *self, PyObject *name)
  11. {
  12.     PyTypeObject *tp = self->ob_type;
  13.     PyObject *getattr, *getattribute, *res;
  14.     static PyObject *getattribute_str = NULL;
  15.     static PyObject *getattr_str = NULL;
  16.  
  17.     if (getattr_str == NULL) {
  18.         getattr_str = PyString_InternFromString("__getattr__");
  19.         if (getattr_str == NULL)
  20.             return NULL;
  21.     }
  22.     if (getattribute_str == NULL) {
  23.         getattribute_str =
  24.             PyString_InternFromString("__getattribute__");
  25.         if (getattribute_str == NULL)
  26.             return NULL;
  27.     }
  28.     getattr = _PyType_Lookup(tp, getattr_str);
  29.     if (getattr == NULL) {
  30.         /* No __getattr__ hook: use a simpler dispatcher */
  31.         tp->tp_getattro = slot_tp_getattro;
  32.         return slot_tp_getattro(self, name);
  33.     }
  34.     getattribute = _PyType_Lookup(tp, getattribute_str);
  35.     if (getattribute == NULL ||
  36.         (getattribute->ob_type == &PyWrapperDescr_Type &&
  37.          ((PyWrapperDescrObject *)getattribute)->d_wrapped ==
  38.          (void *)PyObject_GenericGetAttr))
  39.         res = PyObject_GenericGetAttr(self, name);
  40.     else
  41.         res = PyObject_CallFunction(getattribute, "OO", self, name);
  42.     if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
  43.         PyErr_Clear();
  44.         res = PyObject_CallFunction(getattr, "OO", self, name);
  45.     }
  46.     return res;
  47. }
  48.  

上が__getattribute__、下がgetattr。ま、こんなことやったって関数呼び出しのオーバーヘッドが一番ツライのでgetterは使わず、setterは必要があるときだけ使うように。これで結構はやくなる。

実行ループ

CPUのエミュレータなんてのはだいたい同じようなパターンで割り込みを除いて簡単に書くと

python code
  1.   def step_execute(self, clocks):
  2.     while (self.passed_clocks < clocks):
  3.       opecode = read()# opecodeを取得
  4.       # 実行
  5.       count = CLOCK[opecode] # 実行に必要なクロック数を取得
  6.       self.passed_clocks += count
  7.     self.passed_clocks -= clocks
  8.  

なんてのになるわけで、ここが激しくループするわけで。ここはガリガリにちょっとでも節約できるものは節約。

python code
  1.   def step_execute(self, clocks):
  2.     read = self.memory.read
  3.     get_method_by_opecode = self.get_method_by_opecode
  4.     while (self.passed_clocks < clocks):
  5.       old = self._PC; self._PC += 1; self._PC &= 0xffff
  6.       opecode = read(old)
  7.       method = get_method_by_opecode(opecode)
  8.       count = method()
  9.       count = count != None and count or CYCLES[opecode]
  10.       self.passed_clocks += count
  11.     self.passed_clocks -= clocks
  12.  

javascriptで.(ドット)演算が遅いとかいうのは最近(?)よく言われていることで、それはPythonにも当然当てはまる。なのでループ前にだせるものはローカルに出しておく。(これが簡単にできるのがRubyよりもPythonがいい部分だよなあ)

あと、実行の部分。ここはCなら関数テーブルかswitch(コンパイラによるけど大差ないと思う)になるんだけど、あいにくPythonにはswitchがないので関数テーブル的なものかif opecode == 0x01: ... elif opecode == 0x02:... elif...かになる。

関数テーブル的なものは

python code
  1. def ope_0x01(self):
  2.   #code
  3. def ope_0x02(self):
  4.   #code
  5.  
  6. self.__getattribute__("ope_"+hex(opecode))()
  7.  

となる。両方やってみたところ、大差はなかったので、関数テーブル的なほうに。

結局

そんな感じでいじってみてかつpsycoを入れて1Frame:0.15程度(PPUやAPUは中身がないので、最後までつくったらもっと遅くなる)。ほかにもいじれそうなところはあって、そこをいじれば0.1は切れそうな感じがしてます。

けど、そこまでやるとPythonである意味がないのも確か。ぶっちゃけ、エミュレータは確実にCが向いている。Javaで書かれているエミュレータもレジスタの値をセットするたびにA & 0xffみたいなことをやっていて、どーもめんどくさい。

じゃぁLLでエミュレーターを書く意味ってなんだ、というと・・・うーん(笑 自己満足以外なにもないでしょうねえ。というわけで自己満足のために、今後もヒマができれば、ちょっとづつ書き進めてみようかなあ。

07.27.08/12am

PythonによるNESエミュレータ開発1

正月だというのにひたすらCPUの命令を実装・・・。ここはひたすら地道な作業が続く。やっぱりこういう単純作業は苦手だ・・・

実装の際にはInfoNES6502 Emulation Packageのソースコード、そしてNES on FPGAのCPUのページに大変お世話になりました。特にNES on FPGAのページは非常に分かりやすくまとめられていて助かりました。

実装方針は前にも書いたとおり「できるだけPythonっぽく」。着々とエミュレーターっぽくないソースが出来上がってきています(笑 エミュレータのソースコードというとそれはもう、C言語でマクロ全開、register指定、inline(もしくはstatic)で出力されるコードを極限まで速く、なんて感じですが全く逆です。分かりやすさ優先。とりあえず書いてみて、ダメそうなら諦めるか頑張ってチューニングするか考えよう。

今日までで、CPUの全命令は実装しました(したつもり)。ただ、Indexed Absolute AddressingのX,Y、そしてIndirect Indexed Addressingでページクロスしたとき1クロック余計に発生する、という件は今のところスルー。

メモリ周りもIO以外はだいたい作ったので、今日の時点で単純なコードなら実行可能に。早速アセンブラを書いてNESASMで.NESファイルを作成。それを読み込んで動かしてみました。とりあえず動くことは動く・・・が全命令が正しく実装されているかはまだ分からないです(笑

というわけで次はデバッグ環境を整えて、命令のテストをしないとなあ。

余談。いやー卒論という重圧がないのは非常にプログラミングがはかどる。別にギリギリなわけでもないのに「卒論完全に出来上がってないのにこんなことしてていいのかなーって感じちゃう」的なものがないし。

07.27.08/12am

About

Author:yuin(http://inforno.net/)

文学部文化学科卒という生粋の文系趣味プログラマ。

主にRuby、Javascript、PHP、JAVA,Python,C,Scala,Schemeなどを使っています。今はPythonな感じかもしれない。今後作曲活動なども復活するかもしれない。

Pages