Sleipnir2
いよいよですねー、Sleipnir2。ずっと Opera だったのですが、最近どうも挙動が怪しく(おそらくビデオカードかドライバとの相性か何かだと思いますが)、最近は Sleipnir を使っていました。1.66 は欲しい機能が無い場合があり(例えばマウスで左押しながら右押しで進むとか)、オペレーションでストレスを感じることもあったのですが、それが Slepinir2 では解消されつつあるので常に最新版をテストしています。
未実装なのか実装予定が無いのか判断が出来ない所も多々ありますが、現時点での最新版はかなり使えるレベルまで完成されています。あとブックマークの個別設定が出来るよういなれば個人的に常用可能かなと思い、日々実装されるのを心待ちにしている状態です。
自作のタブブラウザも作ってはいますが最近は放置気味…。IEコンポーネントって結構使いづらいですねぇ。新しいウィンドウで開くと IE が立ち上がります(^^; COM を本格的に勉強する必要があるっぽいです。
何はともあれ正式版の公開が待ち遠しいです! 開発頑張って下さい>フェンリル様
8Bit で表現出来る整数の範囲
昨日の続きですが、Z80 は 8Bit CPU なので基本的な計算は 8Bit で行います。8Bit のレジスタを二つセットにして 16Bit の計算も行えますが、基本は 8Bit です。
さて、この 8Bit で表現出来る範囲は以下の2通りです。
- 符号無し 8Bit 0 〜 255
- 符号付き 8Bit -128 〜 +127
内部的な表現は 0x00 〜 0xFF で全く一緒ですが、人間が符号付きと符合無しを区別することでマイナスも表現出来るようになる訳です。
今回のエミュレータでは、8Bit の数値を byte 型で表現しています。byte 型は 0 〜 255 の符合無し整数です。試しに以下のコードを実行してみます。
byte b = 0; b--;
実行すると b は 255。0 以下は表現できませんので、オーバーフローが発生して最大値である 255 になってしまうんですね。
int i = -1; byte b = (byte)i;
これも同じです。int の -1 は 0xFFFFFFFF ですが、キャストによって余分なビットは全て切り捨てられて、正しく変換されます。
Z80 エミュレータを作る上では byte 型で数値を扱うことで特に意識することなく Z80 チックな処理を記述できますが、オーバーフローを確実にチェックする(キャリーフラグに反映させる)必要があります。
余談ですが byte 型だとあっと言う間にオーバーフローが発生してしまいますが、int 型でも気を抜くとオーバーフローしてしまったりしますね。この手のバグはなかなか原因を発見出来なかったりしますので、プログラムを作る上で気を付けておかないと痛い目に会うことがあります。C# はオーバーフローを検出する機能(例外を生成出来る)もありますので、これを使ってチェックすることは可能です。今回のプログラムでは byte 型や ushort 型のオーバーフローは仕様ですので unchecked 構文の(数少ない^^;)出番となります。やっぱ C# は良いですね〜。
Z80 を再現してみる
フラフラとネットの海をさまよっていたら、とあるページで Z80 という文字を発見。そーいえば昔少しだけマシン語をやったことがあるな〜、懐かしいなぁなんて思い出しニーモニック表を検索で探して読みふけってしまいました。こういうのが速攻で手に入るのは有り難いですねぇ。感謝。
んで、そんなページを見ながら「Z80 のエミュレーションくらいならオレでも簡単に出来るんじゃねーの?」なんてことを思ってしまったので、早速取り掛かることに。
言語は C# を利用、Z80 は一応フルスペックというか割り込み、未定義命令辺りは使えるようにして、簡単な IO(テキストRAMとディスクアクセス、キー入力くらい)も実装する、ということで考え中。互換性は特に必要無いので、IO はかなり適当になる予定。
続きを読むループ処理には DoEvents()
C#に限った話ではないですが。
ループ処理を行うと、その間ウィンドウの操作等を全く受け付けなくなります。例えばあるディレクトリの配下にあるファイルの一覧を取得するプログラムを書いた時、場合によってはかなり深い階層を辿って一覧を作ったりますよね。この場合、ループ処理がなかなか終わらないのでそのあいだ他の処理が全く出来なくなります*1。
これを避ける為に Application.DoEvents() をループ処理の中に入れてやる訳です。この DoEvents() はウィンドウの操作等のメッセージがキューに溜まってればそちらを処理しろ、という命令です。しかし、キューにメッセージが溜まりまくってたら、もとのループ処理に戻って来るまでかなり時間が掛かったりします。逆に DoEvents() から DoEvents() までの間の処理時間が長いと、その間のウィンドウの操作等が思ったように出来なくなったりします。出来ないよりは全然マシではありますが。
うまく使えば応答速度をそれなりにすることが出来るので、マルチスレッド化する前にまず DoEvents() で出来ないかを検討することが必要です*2。どうしても DoEvents() では無理ということになれば、その時改めてスレッドを分ければ良いのですから。
マルチスレッド
とりあえず実験です。
C# のマルチスレッドは何種類か実現方法があるのですが、一番簡単なスレッドプールを使ってみます。
private static object lockObject = new Object(); private static void threadMethod(object o) { // 非同期で実行される部分 lock(lockObject) { // lock による排他処理 } } private void button1_Click(object sender, EventArgs e) { WaitCallback wcb = new WaitCallback(threadMethod); ThreadPool.QueueUserWorkItem(wcb); }
細かい動作の原理は良く分かりませんが、とりあえずこんな感じです。
んで、lock によって lockObject のロックを取得するのですが、これは lockObject へのアクセスを出来なくなるのでは無く、他のスレッドでロックが取得出来ないようにする為の仕組みです。結局人間が処理を把握しておかないと駄目なんですね。当たり前ですが、重要なポイントです。Delphi の Synchronized はメインスレッドで動作させる為の仕組みなので、この辺りの考え方が異なります。どっちが良いという話では全く無いですが。
でも、結構手軽に扱えますね〜。同期を取るのはそれなりに面倒ですが、別にクラスを作る必要が無いってのはポイント高いですね。
ジェスチャ判定方法
こんな感じでどうでしょうか。
// マウスジェスチャ 方向 private enum GestureDirection { Up = 1, Down = 2, Left = 4, Right = 8, None = 0, } void Document_MouseUp(object sender, HtmlElementEventArgs e) { if (e.MouseButtonsPressed == (MouseButtons.Left | MouseButtons.Right)) { Debug.WriteLine("Gesturing Off"); } else { if (e.MouseButtonsPressed == MouseButtons.Right) { mouseGestureRightDown = false; } else if (e.MouseButtonsPressed == MouseButtons.Left) { mouseGestureLeftDown = false; } } if (mouseGestureStatus == GestureStatus.Gesturing) { int gestureCode = getGestureCode(gestureList); if (gestureCode == 4) { // ← GoBack(); } else if (gestureCode == 8) { // → GoForward(); } else if (gestureCode == 40) { // ↓→ Debug.WriteLine("Window Close"); } mouseGestureStatus = GestureStatus.None; gestureList.Clear(); } } private int getGestureCode(List<GestureDirection> gesture) { int result = 0; foreach (int g in gesture) { result = (result << 4) + g; } return result; }
4bit を 1 個のジェスチャとして判定する方法です。符号付32bit でも 7 個まで判定出来るので必要十分な気がします。3bit でも良かったのですが、なんとなく中途半端な気がしたので 4bit にしてしまいました。None がある為(必要かどうかは分かりませんが)残念ながら 2bit には収まりません。
とか書いてたら string を使えばそんな制限無しで判定出来るということに気が付きました…。しかも判定文が
if (gestureList.CompareTo("DR") == 0)
とか書けるので見た目も人にやさしくなりますね(この例は↓→)。本番はこっちを使うかな。