第五回講義 「カーソル移動—前編— キー入力判定」

さてさて、1年ぶりの開講ですね。長々とさぼってました。

今回の講義内容は前回宣言した内容とは少しだけ変えて、まずはキー入力判定から進めたいと思います。


まず、キー入力というものの必要性は言うに及ばないでしょう。

Keyboardというものは、現在のComputerでは最も重要なUser Interfaceの1つで、様々な情報の入力には欠かせないものとなっています。

特にCursorというのは、現在自分がどの情報を参照し、どこに必要な情報を書き込む或いは選択するべきかを示したもので、画面上に展開された種々の情報の選別には欠かせないものです。

という事で、今回はカーソルの移動よりも先にキー入力判定というものを本題に掲げました。


さてこのキー入力の方法というのには幾つかの方式が考えられます。

大体以下の方法があるでしょう。


No.

手段

コメント

1

I/O Portの直接制御

電算機の直接制御を目的とするには適するが、Auto Repeatなども全て自身の手で行わなければならず、またKey Bufferの処理の仕方などの問題や、更にKeyboard Deviceの初期化などに苦労を要する場合もあり、あまり実用的とはいいきれないし、BIOSとの両立が困難な場合がある。

2

BIOSを使用

どちらかというと、BIOSを利用した方がKeyboard Deviceの制御には適すると言える(Keyboardは一旦BIOSを経由してからUserに対する反応が示されるようになっているからです)。


という事で今回の講義内容は2つのテーマで進めますが、特に後者の方を重要視して進めています。


まず、I/O Portの直接制御によるものですが、PC-9800 Seriesでは特に適さない方式なのかもしれません。上級の技術を持ち、Hardwareに対する知識・BIOSに対する知識が十分に備わっているのであれば問題はないのですが、Keyboard Deviceの初期化には数段階の要素があり、また各段階毎のタイミングの取り方など、あまりにも初心者がするには難しい内容を含むからです。まぁ、本講義ではそこまでの高度なものは考えていませんので、そういう事は割愛します(^^;

逆に、PC-88VA SeriesではKeyboard DeviceのInitializeは全てBIOSが一手に握っており、我々Programmerがそこまで意識する必要はないようになっています。

さてこのようなHardwareの違いはありますが、最終目標としては単にI/O Portから直接Keyboardの状況、特に押しているKeyの状態判別を読み取る事にあります。


大抵の内部資料を記した書籍でれば、Keyboard上の各KeyがどのI/O Portに反映されるようになっているのかを示していますが、そういうのを参考にしながら以下の事を考えられるといいでしょう。


;Sample Program1 For PC-88VA
        Ideal
        P186
        Model  Small
        Stack  0200h

CodeSeg

InputCheck:
        in      al,002h         ;I/O Port Read
        test    al,000000010b   ;Press 'A' Check
        jnz     InputCheck

        mov     ax,04c00h       ;Return to PC-Engine(or MS-DOS)
        int     021h

        End


この方法では単に「A」を押したらApplicationが終了するようになっています。

内容は特に簡単で、常にA KeyをMapしているI/O Portを読み込み、A Keyが押されたというFlagが立てば終了するというだけで、難しい事は何もしていません。尚PC-9800 Series用がないのは、諸般の事情から動作確認がとれなくなった為と、手元にそのようなLibralyがないため、動作保証が全くとれないものを掲載するべきではないと考えた為です。

複数のKeyを同時に判別したいのであれば、複数のI/O Portを読み込み、1つずつ認識していけばいい事になります。


さて次にBIOSを利用した場合についてですが、これは実にSimpleにできます。

Keyboard Device InitializeからStatus Readまで全てBIOSに任せればよく、先程と同じ動作をするものを組んでみましょう。


;Sample Program2 For PC-88VA
        Ideal
        P186
        Model  Small
        Stack  0200h

CodeSeg

        mov     ax,00500h       ;Keyboard Initialize
        int     082h
InputCheck:
        mov     ax,00d02h       ;Keyboard Status Read
        int     082h
        test    ah,000000010b   ;Press 'A' Check
        jz      InputCheck

        mov     ax,00400h       ;Key Buffer Clear
        int     082h

        mov     ax,04c00h       ;Return to PC-Engine(or MS-DOS)
        int     021h

        End


;Sample Program2 For PC-9800
        Ideal
        P186
        Model  Small
        Stack  0200h

CodeSeg

        mov     ax,00300h       ;Keyboard Initialize
        int     018h
InputCheck:
        mov     ax,00403h       ;Keyboard Status Read
        int     018h
        test    ah,000100000b   ;Press 'A' Check
        jz      InputCheck

KeyBufferClearLoop:
        mov     ax,00100h       ;Key Buffer Clear
        int     018h
        or      bh,bh
        jz      KeyBufferClearEnd
        xor     ax,ax
        int     018h
        jmp     Short KeyBufferClearLoop

KeyBufferClearEnd:
        mov     ax,04c00h       ;Return to MS-DOS
        int     021h

        End


多少長くなったように思うかもしれませんが、それはBIOSを使う為ですので仕方がありません。

因みに、I/O Portの直接制御でもKeyboard BIOSのInitiailzeを利用すれば、Keyboard Device Initializeができますので、それを利用しても問題はありません。

ただ両Programの結果に大きな違いがあると思います。

前者の方では画面に「A」と表示されるのに対し、後者の方では何も表示されていません。これはKeybuffer Clearを最後にかけているからです。

I/O Portだけを利用する場合にはどうしてもBIOSにためられた情報を抑え切れないのに対し、うまくBIOSを利用すればそのような問題はありません。

しかし、I/O Portだけを利用する事に固執せずにBIOSを併用するようにすれば、恐らくにして問題はないでしょう。というより、BIOSというのはあくまで補助動作用のもので、面倒な処理を全て任せてしまうという為のものと考えるべきなのかも知れませんが。


プログラミング時まめ知識その3−DOSによるKeyboard入力判定−

やる事は大変簡単です。DOS Commandを1個用いるだけです。


        Ideal
        P186
        Model  Small
        Stack   0200h

CodeSeg

InputCheck:
        mov     ah,001h
        int     021h
        sub     al,020h
        cmp     al,'A'          ;Press 'A' Check
        jnz     InputCheck

        mov     ax,04c00h       ;Return to PC-Engine(or MS-DOS)
        int     021h

        End


DOS互換のSoftwareなら、こういう方式(或いはそれに近いもの)でやってる事が殆どだと思います。


では次に複数入力の例を示しましょう。


;Sample Program3 For PC-88VA
        Ideal
        P186
        Model  Small
        Stack  0200h

CodeSeg
        call    KeyboardInitialize
MainRoutine:
        call    KeyInputCheck
        test    al,000010000b
        jz      MainRoutine
        call    KeyBufferClear
        mov     ax,04c00h
        int     021h


        Proc    KeyboardInitialize      Near

        push    ax

                mov     ax,00500h
                int     082h

        pop     ax
        ret
        EndP


        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

        End


;Sample Program3 For PC-9800
        Ideal
        P186
        Model Small
        Stack 00200h

.Code
        call    KeyboardInitialize
MainRoutine:
        call    KeyInputCheck
        test    al,000010000b
        jz      MainRoutine
        call    KeyBufferClear
        mov     ax,04c00h
        int     021h


        Proc    KeyboardInitialize      Near

        push    ax

                mov     ax,00300h
                int     018h

        pop     ax
        ret
        EndP


        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

        End


この方法でやっている事は、Table処理を利用しているというぐらいで大した事は何もしていません(尚、Stop KeyとCursor Keyの判別をしていますが、Stop Keyを押さない限り終了しませんし、反応はないようになっています)。

またBIOSによる入力判定をしていますが、これをI/O Portによるものと入れ替えても、そう動作に問題はないでしょう。というより、前述してきた方法はあくまで別々に行った場合を取り上げただけで、実際にはやりたい事だけを限定すれば、ここまで簡単にできるという事を示したかっただけのものです。

さて、Tableにかかれているものですが、前者はKey Mapの何れのAdderssを読むのか、を示し、後者の列はそのAddressの何Bit目を読むのかを示しています。

この方法を利用すれば、ほぼ大抵の単一のKey入力には応用できます。

現実私も多用する程のものです。


さて、今回の講義内容はまずはここまでとします。

次回は実際にCursorを表示し、Cursor Keyによって移動する所までを示しましょう。


Back


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

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

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