ページへ戻る

− Links

 印刷 

guide​/wintro0003 のバックアップソース(No.4) :: OSASK計画

osaskwiki:guide/wintro0003 のバックアップソース(No.4)

« Prev[4]  Next »[5]
* wiki版のintroシリーズ no.0003
-(by [[K]], 2005.01.28)
-これはC言語でもASKAでもnaskでもとにかくごちゃまぜでintroしてしまおうというこころみ。
--これは[[guide/wintro0002]]の続きなので、それを読んでない人はそれを読んでね。
---最初から読みたい人は[[guide/ASKA]]や[[guide/nask]]からどうぞ。
--C言語をやる人はintroaを読むといいでしょう。ここのプログラムは、introaと同一内容です。
-質問とか感想とかはこめんと欄にレッツゴー。

*** 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 /* rigth */:
             if (x < 19)
                 x++;
             break;

         case 6 /* up */:
             if (y >  0)
                 y--;
             break;

         case 7 /* down */:
             if (y <  7)
                 y++;
             break;

         case 8 /* chagne */:
             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の動作が不安定になります。ですからやってはいけません。

*** まとめ
-これで時間を細かく制御できるようになるはずです。時間とともに表示を操作すれば動きのある表示(アニメーション)ができるでしょう。ここまでのところではテキストしか扱えないので、テキストアニメーションしかできませんが。それでも、少しは遊べるはずです。もねさんがすぐに「msg00」や「msg01」を作ったのもうなずけます。
-精密な時間制御が比較的簡単にできることはOSASKアプリの特徴の一つです。他のOSよりも簡単にマスターできることでしょう。

*** おまけ
-次回(no.0003)はキー入力を少しやります。これで、キー入力を反映できるようになるでしょう。
-読んでいる人が追いつけないほど先に進んでもしょうがないので、こめんと欄でリクエストがきたら次回を書き始めます。

* こめんと欄
-シグナルとは何でしょう?APIリファレンスも見ていましたが、シグナルの概念がよく分かりません(汗。 -- ''nika'' SIZE(10){2005-02-02 (水) 04:11:58}

#comment

« Prev[4]  Next »[5]