手元に実機も実行環境もありませんのでサポート不可です。
文章は執筆当時のものをそのまま掲載していますので、時代・時節と合わない表現が含まれています。
また、一部に半角カナ文字が使用されています。
さてさて前回の講義内容の続きである「カーソル移動 後編」となりましたが、今回はその核心的内容に迫ります。
とはいえ、実際にはここでは今までの講義内容を全て理解していれば、比較的容易に解るような内容です。
と、いいますのも、まず文字の表示に関しては第四回講義の最後に説明した任意の座標に文字列を表示するというものを、キーボード入力については前回の内容で十分に説明がついているからなのです。
他にも数点あるにはあるのですが、それらは追々説明していくとしましょう。
まずはいきなりですが、例を示しましょう。
(ちなみにこの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は殆ど取り扱わない事にしていますので、割愛させていただきます)。
次回は、「単純なゲームを作る」です。
尚、次の内容で殆ど第一講義室の講義は終了となります。
手元に実機も実行環境もありませんのでサポート不可です。
文章は執筆当時のものをそのまま掲載していますので、時代・時節と合わない表現が含まれています。
また、一部に半角カナ文字が使用されています。