こんにちは、実際の時間/分/秒を表示しようとしています。これは私のコードサンプルです:
MOV AH, 2Ch
INT 21h
MOV AH, 0Eh
MOV AL, CH
INT 10h
MOV AL, 3Ah
INT 10h
MOV AL, CL
INT 10h
MOV AL, 3Ah
INT 10h
MOV AL, DH
INT 10h
ret
ここで、コンソールに表示されている内容を確認できます
命令セットのリファレンス マニュアルについては、x86タグのwikiを参照してください。参考資料やチュートリアルへの多くの優れたリンクもあります。
整数を関数に分解する必要がある ASCII 数字に分割するには、十分なコードが必要です。
これは、@hobbs の print2Digits 関数の最適化およびバグ修正されたバージョンです。(私も彼の答えでバージョンをバグ修正したので、それも正しいですが、これには最適化を残しました)。
print2Digits:
;; input in AL (0-99). (Or preferably already zero-extended to AX so we can omit CBW)
;; clobbers AX and DX
cbw ; zero AH. Sign-extending AL does the job because AL is only allowed to be 0-99.
mov dl, 10
div dl ; quotient in AL(first (high) digit), remainder in AH(second (low) digit)
add ax, 0x3030 ; add '0' to al and ah at the same time.
mov dl, ah ; save the 2nd digit
mov ah, 0x0E ; BIOS call #: print single character
int 0x10 ; print high digit first. Doesn't clobber anything, so AH still holds 0x0E after
mov al, dl
int 0x10 ; print the low digit 2nd
ret
以前div
は整数を base10 の 2 桁に分割していたのでah
、ゼロにする必要があります。つまり、配当が AX にあり、AH にガベージがある可能性がある AL だけではありません。cbw
or mov ah,0
if the caller did movzx ax, ch
or something をゼロに保存できますah
。
(ただし、8086 には がないためmovzx
、実際にはxor ax,ax
/が必要ですmov al, ch
。)
文字列全体を出力するための DOS システム コールがあるため、このAMD64 Linux FizzBuzzで行っているように、文字を小さなバッファーに格納して一度にすべて出力することができます。c ライブラリから printf を使用せずに、アセンブリ レベル プログラミングで整数を出力するにはどうすればよいですか?も参照してください。バッファ関数のより一般的な int->string 、またはx86 タグ wikiの他の複数桁の数値リンクについて
AL (AX の代わりに) を 10 で除算するために使用aam
することもでき、最初に AH をゼロにする必要がなくなります。div r8
現在の Intel および AMD CPUよりもわずかに高速です。ただし、結果は とは逆のレジスタに格納されます。div
つまり、 の後に余分な命令が追加されaam
ます。mov dl, 10
これにより、との節約が相殺されcbw
ます。
print2Digits:
;; input in AL (0-99). (Ignores AH because we use AAM instead of div)
;; clobbers AX and DX
aam ; like `div` by 10, but with the outputs reversed, and input from AL only
;; quotient in AH (high digit), remainder in AL(low digit). (Opposite to div)
add ax, 0x3030 ; add '0' to al and ah at the same time.
mov dl, al ; save the low digit
mov al, ah ; print high digit first
mov ah, 0x0E ; BIOS call #: print single character
int 0x10 ; print first digit. Doesn't clobber anything, so AH still holds 0x0E after
mov al, dl
int 0x10 ; print second digit
ret
文字列に保存したい (そして文字列出力関数またはシステム コールを 1 回呼び出す) 場合でも、AX をメモリに保存する前に al と ah を交換するxchg al,ah
必要があります (たとえば、最新のハードウェアではより効率的ですが、 186: rol ax,8
)。 div
AX 内で正しい順序でそれらを生成します。
32ビットのアドレスサイズが利用可能な386の場合、1つの命令を節約できます。
lea dx, [eax + 0x3030] ; need a 32bit addressing mode to use eax as a source reg. Adds '0' to both digits at once, with a different destination.
mov al, dh ; then get ready to print the high byte first
はlea
、アドレス サイズのプレフィックスと 2 バイトの mod/rm、および 32 ビットのディスプレースメントを必要とするため、コード サイズが大きく失われますが、1 つの命令を節約できます。
書き込み後lea
の読み取りに使用すると、おそらく Sandybridge ファミリーの CPU でより高速になります。Haswell およびそれ以降のバージョンですが、Intel pre-SnB では、部分的なレジスタ ストールにより、純粋な 16 ビット バージョンを別の add 命令と mov 命令で使用する方が適切です。eax
div
ax
もちろん、実際にパフォーマンスを気にするのであれば、実際に 10 で割る代わりに乗法逆数を使用するでしょう。 また、通常は、従来の BIOS 呼び出しを行う 16 ビット コードを作成することもありません!