第六回講義 「カーソル移動—後編— カーソル移動」

さてさて前回の講義内容の続きである「カーソル移動 後編」となりましたが、今回はその核心的内容に迫ります。

とはいえ、実際にはここでは今までの講義内容を全て理解していれば、比較的容易に解るような内容です。


と、いいますのも、まず文字の表示に関しては第四回講義の最後に説明した任意の座標に文字列を表示するというものを、キーボード入力については前回の内容で十分に説明がついているからなのです。

他にも数点あるにはあるのですが、それらは追々説明していくとしましょう。


まずはいきなりですが、例を示しましょう。

(ちなみにこの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は殆ど取り扱わない事にしていますので、割愛させていただきます)。


次回は、「単純なゲームを作る」です。

尚、次の内容で殆ど第一講義室の講義は終了となります。


Back


手元に実機も実行環境もありませんのでサポート不可です。

文章は執筆当時のものをそのまま掲載していますので、時代・時節と合わない表現が含まれています。

また、一部に半角カナ文字が使用されています。