wiki版のintroシリーズ no.0003
- (by K, 2005.01.28)
- これはC言語でもASKAでもnaskでもとにかくごちゃまぜでintroしてしまおうというこころみ。
- これはguide/wintro0002の続きなので、それを読んでない人はそれを読んでね。
- 最初から読みたい人はguide/Cやguide/ASKAやguide/naskからどうぞ。
- C言語をやる人はintroaを読むといいでしょう。ここのプログラムは、introaと同一内容です。
- これはguide/wintro0002の続きなので、それを読んでない人はそれを読んでね。
- 質問とか感想とかはこめんと欄にレッツゴー。
move
- C版:"movec2.c" [tolset08では 681バイト]
/* "movec2.c" */ /* stack:4k malloc:2k */ #include <guigui00.h> #define AUTO_MALLOC 0 #define REWIND_CODE 1 static int *signalbox0, *sig_ptr; void initsignalbox(); int getsignal(); void OsaskMain() { struct LIB_WINDOW *window; struct LIB_TEXTBOX *wintitle, *textbox; int x, y, c; /* ライブラリ初期化 */ lib_init(AUTO_MALLOC); /* シグナルボックス初期化 */ initsignalbox(); /* ウィンドウオープン */ window = lib_openwindow(AUTO_MALLOC, 0x0200, 20 * 8, 8 * 16); wintitle = lib_opentextbox(0x1000, AUTO_MALLOC, 0, 6, 1, 0, 0, window, 0x00c0, 0); textbox = lib_opentextbox(0x0001, AUTO_MALLOC, 0, 20, 8, 0, 0, window, 0x00c0, 0); lib_putstring_ASCII(0x0000, 0, 0, wintitle, 0, 0, "movec2"); /* シグナル定義 */ lib_definesignal1p0(3, 0x0100, 0x00ac, window, 4); /* カーソルキー(計4つ) */ lib_definesignal1p0(0, 0x0100, ' ', window, 8); /* スペースバー */ lib_definesignal0p0(0, 0, 0, 0); /* まず表示 */ x = 9; y = 3; c = 11; lib_putstring_ASCII(0x0000, x, y, textbox, c, 0, "O"); /* メインループ */ for (;;) { int ox = x, oy = y, oc = c; switch (getsignal()) { case 0: /* シグナルがないので、シグナルが来るまでスリープ */ lib_waitsignal(0x0001, 0, 0); continue; case 4 /* left */: if (x > 0) x--; break; case 5 /* right */: if (x < 19) x++; break; case 6 /* up */: if (y > 0) y--; break; case 7 /* down */: if (y < 7) y++; break; case 8 /* change */: c = (c + 1) & 0x0f; break; } /* 何らかの変化があれば表示し直す */ if (x != ox || y != oy || c != oc) { lib_putstring_ASCII(0x0000, ox, oy, textbox, 0, 0, " "); lib_putstring_ASCII(0x0000, x, y, textbox, c, 0, "O"); } } } void initsignalbox() /* シグナルボックス初期化 */ { sig_ptr = signalbox0 = lib_opensignalbox(256, AUTO_MALLOC, 0, REWIND_CODE); return; } int getsignal() /* 0が返されたら、シグナルなし */ { int signal; if (*sig_ptr == REWIND_CODE) { /* REWINDシグナルを受け取った */ /* 直後の値の分だけシグナルを処理したことにして、ポインタを先頭に戻す */ lib_waitsignal(0x0000, *(sig_ptr + 1), 0); sig_ptr = signalbox0; } signal = *sig_ptr; if (signal != 0) { sig_ptr++; /* 1シグナル受け取ったことをライブラリに通知 */ lib_waitsignal(0x0000, 1, 0); } return signal; }
- (以下未編集)
- ASKA版:"cntdwna1.ask" [osa_dir2では 249バイト]
/* "cntdwna1.ask":countdwnをASKAで記述した例 */ /* stack:4k malloc:0k */ segment CODE(USE32, PARA); default(code == CODE); asmout("[FILE 'cntdwna1.ask']"); asmout("GLOBAL _OsaskMain"); void setdec3(); /* EAXをDS:ESIに書き込む */ void _OsaskMain() { asmout("MOV EBX,data.init"); CALL(0xc7, 0); for (;;) { unsigned int count == EAX; count = 100; do { asmout("MOV EBX,data.putnum"); LEA(ESI, [EBX + 36]); setdec3(); CALL(0xc7, 0); count--; } while (!= 0); asmout("MOV EBX,data.putgo"); CALL(0xc7, 0); } } void setdec3() { char *s == DS:ESI; PUSH(EDX); PUSH(ECX); PUSH(EAX); EDX = 0; ECX = 10; DIV(ECX); /* EDX:EAX / ECX = EAX ... EDX */ DIV(CL); /* AX / CL = AL ... AH */ EAX |= 0x3030; /* '00' */ DL |= 0x30; if (AL == 0x30) { AL = 0x20; /* ' ' */ if (AH == 0x30) AH = 0x20; } s[0] = AL; s[1] = AH; s[2] = DL; POP(EAX); POP(ECX); POP(EDX); return; } asmout("[SECTION .data]"); void data() { ALIGNB(4); init: asmout("DD 0x0004, data.work"); /* lib_init */ asmout("DD 0x0020, data.window, 0x0200, 18 * 8, 1 * 16"); /* lib_openwindow */ asmout("DD 0x0028, 0x1000, data.wintitle, 0, 8, 1, 0, 0, data.window, 0x00c0, 0"); /* lib_opentextbox */ asmout("DD 0x0028, 0x0000, data.textbox, 0, 3, 1, 56, 0, data.window, 0x00c0, 0"); /* lib_opentextbox */ asmout("DD 0x0040, 0x1000, 0, 0, data.wintitle, 0, 0, 0, 8, 'cntdwna1'"); /* lib_putstring1 */ DD(0x0000); /* end of functions */ putnum: asmout("DD 0x0040, 0x1000, 0, 0, data.textbox, 0, 0, 0, 3, '000'"); /* lib_putstring1 */ DD(0x0018, 0x0007, 0, 0, 0, 1, 0); /* lib_waitsignaltime */ DD(0x0000); /* end of functions */ putgo: asmout("DD 0x0040, 0x1000, 0, 0, data.textbox, 0, 0, 0, 3, 'GO!'"); /* lib_putstring1 */ DD(0x0018, 0x0007, 0, 0, 0, 10, 0); /* lib_waitsignaltime */ DD(0x0000); /* end of functions */ ALIGNB(8); work: RESB(256); /* func_initに必要な256バイトのワークエリア */ window: RESB(128); /* ウィンドウ構造体 */ wintitle: RESB(64); /* テキストボックス構造体 */ RESB(64); /* 8x1文字分 (8 * 1 * 8) */ textbox: RESB(64); /* テキストボックス構造体 */ RESB(24); /* 3x1文字分 (3 * 1 * 8) */ }
- nask版:"cntdwnn1.nas" [osa_dir2では 251バイト]
; "cntdwnn1.nas":countdwnをnaskで記述した例 ; stack:4k malloc:0k [FORMAT "WCOFF"] [INSTRSET "i486p"] [OPTIMIZE 1] [OPTION 1] [BITS 32] [FILE "cntdwnn1.nas"] [SECTION .text] GLOBAL _OsaskMain _OsaskMain: MOV EBX,init CALL 0xc7:0 .mainloop: MOV EAX,100 .countloop: MOV EBX,putnum LEA ESI,[EBX+36] CALL setdec3 CALL 0xc7:0 DEC EAX CMP AL,16 JNZ .countloop MOV EBX,putgo CALL 0xc7:0 JMP .mainloop setdec3: PUSH EDX PUSH ECX PUSH EAX XOR EDX,EDX MOV ECX,10 DIV ECX DIV CL OR EAX,'00' OR DL,'0' CMP AL,'0' JNE .skip MOV AL,' ' CMP AH,'0' JNE .skip MOV AH,' ' .skip: MOV [ESI+0],AL MOV [ESI+1],AH MOV [ESI+2],DL POP EAX POP ECX POP EDX RET [SECTION .data] ALIGNB 4 init: DD 0x0004, work ; lib_init DD 0x0020, window, 0x0200, 18 * 8, 1 * 16 ; lib_openwindow DD 0x0028, 0x1000, wintitle, 0, 8, 1, 0, 0, window, 0x00c0, 0 ; lib_opentextbox DD 0x0028, 0x0000, textbox, 0, 3, 1, 56, 0, window, 0x00c0, 0 ; lib_opentextbox DD 0x0040, 0x1000, 0, 0, wintitle, 0, 0, 0, 8, 'cntdwnn1' ; lib_putstring1 DD 0x0000 ; end of functions putnum: DD 0x0040, 0x1000, 0, 0, textbox, 0, 0, 0, 3, '000' ; lib_putstring1 DD 0x0018, 0x0007, 0, 0, 0, 1, 0 ; lib_waitsignaltime DD 0x0000 ; end of functions putgo: DD 0x0040, 0x1000, 0, 0, textbox, 0, 0, 0, 3, 'GO!' ; lib_putstring1 DD 0x0018, 0x0007, 0, 0, 0, 10, 0 ; lib_waitsignaltime DD 0x0000 ; end of functions ALIGNB 8 work: RESB 256 ; func_initに必要な256バイトのワークエリア window: RESB 128 ; ウィンドウ構造体 wintitle: RESB 64 + 8 * 1 * 8; テキストボックス構造体(8x1文字分) textbox: RESB 64 + 3 * 1 * 8; テキストボックス構造体(3x1文字分)
説明
- 概要:
- このプログラムは、ウィンドウを一つ開き、そのウィンドウに1秒おきに数字を表示していくだけのプログラムです。
- このプログラムを通じて、一定時間タスクをスリープさせる方法を説明します。
- OSASKでは、アプリケーションが一定の時間を待つために、空のループをまわしたり、時刻を取得して比較を繰り返すようなことはしません。できないというわけではありませんが、CPUタイムの浪費につながりますから、もったいないです。したがって、所定の手続きを踏んで、時間が来るまでタスクをスリープさせます。
- APIの説明:
- lib_waitsignaltime(opt, signaldw, nest, time0, time1, time2) :
- タスクを一定時間スリープさせます。optには、0x7か0xfを指定できます。signaldwとnestは0にしてください。time0~time2の設定方法は、以下のようにしてください。まず、設定したい時間間隔を秒で表します。その整数部をtime1に書き込みます。もし、32bitで書ききれないのでしたら(そんなに長く待つことがあるのかどうかは分かりませんが)、time2に上位32bitを入れてください(time1だけで収まれば、time2はもちろん0です)。つまり、最大で5千万年くらいまでは指定できます(でも今のバージョンでは上位数ビットが生かされないかもしれないのでせいぜい数百万年くらいにしてください・・・笑)。また、秒単位では細かな制御には向かないでしょう。そこで、小数部に2^32を乗じた値をtime0に設定します。これで1ns(ナノ秒)よりも細かい単位で指定できることになります。実際はハードウェアの都合によりこの96bitの精度が完全に生かされるとは限りませんが、しかし限界まで生かす努力はします。
- optに0xfを指定した場合、この関数をコールした時刻から指定された時間だけ待った後にタスクは目覚めます。一方、optに0x7を指定した場合は、既に設定されている時間基点から指定された時間だけ待った後にタスクが目覚めます。時間基点というのは、タスクが起動した時刻のことですが、lib_waitsignaltime()でスリープすると目覚めた時刻が新しい時間基点にセットされます。今回のサンプルのようにできるだけ「秒」を意識するなら、optに0xfを指定するよりは0x7を指定する方がふさわしいでしょう。なぜなら、目覚めた直後にすぐにスリープするわけではなく、文字表示などをするからです。
- この処理を1ns以下に終えることは期待できませんから、0xfを指定すれば徐々にこの誤差が累積されることになります。0x7ならそういうことは原理的にありません。これで誤差が出る場合にはOSかハードウェアの問題であり、アプリケーションの問題ではありません。目的に応じて使い分けましょう。
- 本来はこの関数は一定時間待つためのものではありません。時限付きシグナル待ちというもので、シグナルがくれば時刻に達していなくてもタスクはスリープから回復してしまいます。しかし今のところシグナルを一切扱っていないので、事実上、純粋な時間待ちの処理として利用できます。
- この時間間隔指定ですが、少なくとも10ミリ秒以上はあってほしいです。これは何も10ミリ秒単位であることを要請しているわけでありません。11.52ミリ秒などでもかまいません。また頻繁に繰り替えすことがないのなら、10ミリ秒を下回ってもかまいません。将来のOSASKでは、連続して何度も10ミリ秒以下の指定をした場合、OS側で強制的に10ミリ秒間間隔に補正される可能性があります。現在のOSASKでは、OSの動作が不安定になります。ですからやってはいけません。
- lib_waitsignaltime(opt, signaldw, nest, time0, time1, time2) :
まとめ
- これで時間を細かく制御できるようになるはずです。時間とともに表示を操作すれば動きのある表示(アニメーション)ができるでしょう。ここまでのところではテキストしか扱えないので、テキストアニメーションしかできませんが。それでも、少しは遊べるはずです。もねさんがすぐに「msg00」や「msg01」を作ったのもうなずけます。
- 精密な時間制御が比較的簡単にできることはOSASKアプリの特徴の一つです。他のOSよりも簡単にマスターできることでしょう。
おまけ
- 次回(no.0003)はキー入力を少しやります。これで、キー入力を反映できるようになるでしょう。
- 読んでいる人が追いつけないほど先に進んでもしょうがないので、こめんと欄でリクエストがきたら次回を書き始めます。
こめんと欄
- シグナルとは何でしょう?APIリファレンスも見ていましたが、シグナルの概念がよく分かりません(汗。 -- nika 2005-02-02 (水) 04:11:58
- それはこれから書くことですので、このページが出来上がるまで(=以下未編集の表示が消えるまで)のんびりとお待ちください。 -- K 2005-02-02 (水) 13:16:38
Counter: 268,
today: 1,
yesterday: 0
初版日時: 2005-01-28 (金) 22:21:18
最終更新: 2009-12-01 (火) 00:00:00 (JST) (349d) by lina
|
ぺージ情報 | 閲覧可 | 編集可 | |||
---|---|---|---|---|---|---|
ぺージ名 : | guide/wintro0003 | グループ : | すべての訪問者 | グループ : | すべての訪問者 | |
ページ作成 : | lina | ユーザー : | すべての訪問者 | ユーザー : | すべての訪問者 | |
ページ別名 : | 未設定 |