手元に実機も実行環境もありませんのでサポート不可です。
文章は執筆当時のものをそのまま掲載していますので、時代・時節と合わない表現が含まれています。
また、一部に半角カナ文字が使用されています。
さてさて前回の講義内容の続きである「カーソル移動 後編」となりましたが、今回はその核心的内容に迫ります。
とはいえ、実際にはここでは今までの講義内容を全て理解していれば、比較的容易に解るような内容です。
と、いいますのも、まず文字の表示に関しては第四回講義の最後に説明した任意の座標に文字列を表示するというものを、キーボード入力については前回の内容で十分に説明がついているからなのです。
他にも数点あるにはあるのですが、それらは追々説明していくとしましょう。
まずはいきなりですが、例を示しましょう。
(ちなみにこのSampleの終了方法はStopキーで、カーソルキーによって「A」という文字が移動します。)
;For PC-88VA Series Sample Program Ideal P186 Model Small Stack 00200h CodeSeg Initialize: call TextVRAMChange call KeyboardInitialize mov dl,'A' mov bx,[Word cs:CharacterPoint + 0] mov cx,[Word cs:CharacterPoint + 2] call CharacterPrint MainRoutine: mov dl,'A' ;表示 call CharacterPrint call VSYNCWait call KeyInputCheck or al,al jz MainRoutine test al,000010000b jnz SystemReturn UpCheck: test al,000001000b jz DownCheck or cx,cx jz DownCheck dec cx DownCheck: test al,000000100b jz LeftCheck cmp cx,23 jae LeftCheck inc cx LeftCheck: test al,000000010b jz RightCheck or bx,bx jz RightCheck dec bx RightCheck: test al,000000001b jz ReturnMainRoutine cmp bx,79 jae ReturnMainRoutine inc bx ReturnMainRoutine: jmp Short MainRoutine SystemReturn: call KeyBufferClear mov ax,04c00h int 021h Proc KeyboardInitialize Near push ax mov ax,00500h int 082h pop ax ret EndP ;Return ; AL : Key Push Flag ; None - None - None - Stop = Up - Down - Left - Right Proc KeyInputCheck Near pushf push bx cx ds cld mov ax,cs mov ds,ax mov si,Offset InputKeyTable xor bx,bx mov cx,5 KeyInputCheckLoop: push cx lodsb mov ah,00dh int 082h lodsb mov cl,al shr ah,cl rcl bl,1 pop cx loop KeyInputCheckLoop mov ax,bx pop ds cx bx popf ret EndP InputKeyTable: db 009h,1 ;Stop db 008h,2 ;Up db 00ah,2 ;Down db 00ah,3 ;Left db 008h,3 ;Right Proc KeyBufferClear Near push ax mov ax,00400h int 082h pop ax ret EndP ;Call ; BX : X軸 ; CX : Y軸 ; DL : 表示する文字 Proc CharacterPrint Near push bx cx di es shl bx,1 ;BX := BX * 2 mov di,bx shl cx,5 ;CX := CX * (80 * 2) add di,cx shl cx,2 add di,cx mov bx,0a000h mov es,bx mov [Byte es:di],dl pop es di cx bx ret EndP Proc TextVRAMChange Near push ax dx mov dx,00154h mov al,041h out dx,al pop dx ax ret EndP Proc VSYNCWait Near push ax VSYNCWait1: in al,040h test al,000100000b jz VSYNCWait1 VSYNCWait2: in al,040h test al,000100000b jnz VSYNCWait2 pop ax ret EndP CharacterPoint: dw 40 ;X dw 12 ;Y End
;For PC-9800 Series Sample Program Ideal P186 Model Small Stack 00200h CodeSeg Initialize: call KeyboardInitialize mov dl,'A' mov bx,[Word cs:CharacterPoint + 0] mov cx,[Word cs:CharacterPoint + 2] call CharacterPrint MainRoutine: mov dl,'A' ;表示 call CharacterPrint call VSYNCWait call KeyInputCheck or al,al jz MainRoutine test al,000010000b jnz SystemReturn UpCheck: test al,000001000b jz DownCheck or cx,cx jz DownCheck dec cx DownCheck: test al,000000100b jz LeftCheck cmp cx,23 jae LeftCheck inc cx LeftCheck: test al,000000010b jz RightCheck or bx,bx jz RightCheck dec bx RightCheck: test al,000000001b jz ReturnMainRoutine cmp bx,79 jae ReturnMainRoutine inc bx ReturnMainRoutine: jmp Short MainRoutine SystemReturn: call KeyBufferClear mov ax,04c00h int 021h Proc KeyboardInitialize Near push ax mov ax,00300h int 018h pop ax ret EndP ;Return ; AL : Key Push Flag ; None - None - None - Stop = Up - Down - Left - Right Proc KeyInputCheck Near pushf push bx cx ds cld mov ax,cs mov ds,ax mov si,Offset InputKeyTable xor bx,bx mov cx,5 KeyInputCheckLoop: push cx lodsb mov ah,004h int 018h lodsb mov cl,al shr ah,cl rcl bl,1 pop cx loop KeyInputCheckLoop mov ax,bx pop ds cx bx popf ret EndP InputKeyTable: db 00ch,1 ;Stop db 007h,3 ;Up db 007h,6 ;Down db 007h,4 ;Left db 007h,5 ;Right Proc KeyBufferClear Near push ax bx KeyBufferClearLoop: mov ax,00100h int 018h or bh,bh jz KeyBufferClearEnd xor ax,ax int 018h jmp Short KeyBufferClearLoop KeyBufferClearEnd: pop bx ax ret EndP ;Call ; BX : X軸 ; CX : Y軸 ; DL : 表示する文字 Proc CharacterPrint Near push bx cx di es shl bx,1 ;BX := BX * 2 mov di,bx shl cx,5 ;CX := CX * (80 * 2) add di,cx shl cx,2 add di,cx mov bx,0a000h mov es,bx mov [Byte es:di],dl pop es di cx bx ret EndP Proc VSYNCWait Near push ax VSYNCWait1: in al,0a0h test al,000100000b jz VSYNCWait1 VSYNCWait2: in al,0a0h test al,000100000b jnz VSYNCWait2 pop ax ret EndP CharacterPoint: dw 40 ;X dw 12 ;Y End
要はやっている事は単純で、現在のカーソル位置に「A」を表示しているだけです。
しかしこの方法では前のAを消していないので、何処にあるのかがわからなくなってしまいます。
プログラミング時のまめ知識その4−各機種共通で使えるWait−
特にPC-9800 Seriesというのは、沢山の機種があり、各機種ごとにCPUの動作速度が徐々にあがっていったり、他のDeviceの処理が向上されていたりします。
そういう時に最も困るのが・・・Waitの取り方です。
今回のSample ProgramではVAと98という異なる機種の間でも極力同じWait時間をとれるように図っています。
そのRoutineというのは、VSYNCWait Procedureです。
V-SYNCというのは、CRTの垂直帰線期間(CRTというのは電子銃から出た電子を偏向板で方向を変えて画面全体をくまなく表示できるようにしているのですが、一旦下まで表示してから上まで戻るには偏向板にかかる電圧の調整の為に多少なりとも時間がかかります。この時、CRTに信号を送信すると、表示が崩れる事になります。そこでそれを抑える為に垂直帰線期間を取り出し、その間は割り込みをかけて信号を送信しないようにしています。その割り込み時間(間隔)をV-SYNCといいます。ちなみに、この期間は画面への描画が行われませんので、うまく利用するとチラツキの少ないアニメーション(特にPaletteがらみ)なんかができたりします。あと、H-SYNCというものもあり、これは水平帰線期間といいます。この割り込み間隔も取り出せますが、水平期間は速い為、あまり使用されません。またV-SYNCはIRQにも登録させていたりしますが、H-SYNCはありません)の事をいいます。
これは殆どの場合、同じ時間を要します(実際には画面解像度によるのですが、MS-DOSの画面だけで考えていますので)。
つまり、VAであれ98であれこの時間を取り出し、待つように仕組んでやればいいわけです。
で、V-SYNCはその期間に入るとI/O Portに反映され、割り込みがかかりますが、その時反映されるI/O Portの情報を直接読む事により、期間中か否かが判別でき、期間に入ってから、期間を抜けるまでずっとそのPortを監視していれば、V-SYNC期間のWaitがかけられるようになります。
あと、共通のWaitとしては、Interval Timerや内臓時計を用いる方法などがあります。
という事で、次はMainRoutineの一部を以下のようにしてみましょう。
;Sample Program Step2 MainRoutine: mov dl,'A' ;表示 call CharacterPrint call VSYNCWait mov dl,' ' ;消去 call CharacterPrint call KeyInputCheck or al,al jz MainRoutine test al,000010000b jnz SystemReturn
とこれでは、前のが消えるようになりましたが、常に書いては消してを続ける為チラツキが出てしまいます。
そこでこのチラツキを抑えるにはという事になるのですが、要は移動前に消して、移動後に表示すればいいという事は想像がつくでしょう。
ではどのタイミングで消すのか・・・、という事になるのですが、移動前とは座標計算が始まる直前、移動後とは計算後を指します。
で、座標計算後の表示というのは既に実装済みなのですが、計算前はどこなのか・・・それはキー入力直後にあります。
では再びMainRoutineを以下のようにしてみましょう。
;Sample Program Step3 MainRoutine: call VSYNCWait call KeyInputCheck or al,al jz MainRoutine test al,000010000b jnz SystemReturn mov dl,' ' ;消去 call CharacterPrint ・ ; ・ ;ここは元のままで変更しない ・ ; ReturnMainRoutine: mov dl,'A' ;表示 call CharacterPrint jmp Short MainRoutine
つまり、キー入力判定の結果カーソルキーであった場合にカーソルを消し、座標演算後に表示するようにしています。
ちなみに、画面の端までいくとそれ以上は行けないようにしていますが、その判定部分をちょっとだけ変更する事によりたとえば左端に行くと右端から登場するようにするとかという事も可能です。
(次回講義で取り扱ってみましょう)
この技術を応用する事により、たとえばゲームなどでの自キャラの移動にも利用する事ができます。
また、今回はTextに対しての事を行いましたが、Graphicにも拡張する事ができます(但し本講義ではGraphicは殆ど取り扱わない事にしていますので、割愛させていただきます)。
次回は、「単純なゲームを作る」です。
尚、次の内容で殆ど第一講義室の講義は終了となります。
手元に実機も実行環境もありませんのでサポート不可です。
文章は執筆当時のものをそのまま掲載していますので、時代・時節と合わない表現が含まれています。
また、一部に半角カナ文字が使用されています。