ブートで遊ぼっ!(5)

86系のPCをブートさせて遊んでみよう、というお話の5回目になります。テキストは『OSを書く:初歩から一歩ずつ』です。
環境はAMD64+Windows10+VMware Workstation 17 Player+Debian 12.4です。
前回、練習問題の1をやったので続きを。

2. 2つの数値を加算する。

すでに終わっている気もしないでもないですが、改めて。
1と3を足してみましょう。

; boot.asm
;
mov ax, 0x07c0
mov ds, ax

mov ah, 0x0
mov al, 0x3
int 0x10

mov ah, 0x0E

mov al, 1
mov bl, 3
add al, bl

add al, 0x30

int 0x10

; end of proccess
;
hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

PlayWithBoot.4.1

説明の必要もないくらいですね。alとblにそれぞれ1と3を入れて、alとblを加算。
それだけです。
ただ、結果を確認できないのも何なので前回やった数値を表示するやりかたで、数値の1を文字の「1」に変換して表示しています。alに0x30を足すところですね。
これを実行すると、こうなります。


3. 1から100までの数を合計して解を画面にプリントする。

ネタばれしてるような気もしますが、こんな感じでソースを書いてみました。

; boot.asm
;
mov ax, 0x07c0
mov ds, ax

mov ah, 0x0
mov al, 0x3
int 0x10

mov ax, 0
mov bx, 0

loop:

add bx, 1
add ax, bx

cmp bx, 100
jl loop

add al, 0x30

int 0x10

; end of proccess
;
hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

PlayWithBoot.4.2

実行すると、こうなります。

えっと、実は数値に0x30を足して文字に変換するって、1文字にしか対応していないんです。0~9は変換できるんですが、10から先には対応していません。
ではどうするか?
『1文字にしか対応できないなら、1文字ずつ表示すればいいじゃない』
はい、その通りです。例えば12を表示するとして、1と2を1文字ずつ表示すればいいんです。ではどうやって?
う~ん、中学受験の点取らせ問題ですね。
10で割った余りが1の位の数値になります。
答えを同じく10で割って余りが次の位の数値になります。
答えを…とまぁ数値の桁数だけ繰り返すわけです。
具体的にはこんな感じ。

12を例にしてソースを書いてみました。

; boot.asm
;
mov ax, 0x07c0
mov ds, ax

mov ah, 0x0
mov al, 0x3
int 0x10

mov cx, 12

mov dx, 0
mov ax, cx
mov bx, 10
div bx
mov cx, ax
mov ah, 0x0e
mov al, dl
add al, 0x30
int 0x10

mov dx, 0
mov ax, cx
mov bx, 10
div bx
mov cx, ax
mov ah, 0x0e
mov al, dl
add al, 0x30
int 0x10

; end of proccess
;
hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

PlayWithBoot.4.3

割り算を行うdiv命令ってなんだか癖があって、axに入れた数値をbxで割って、答えがaxに、余りがdxに設定されます。
今回は10で割るので余りは確実に一桁。答えの下半分であるdlだけ見れば良いということになります。
1の位から上に向かって処理するので、12は21と表示されます。
こんな感じですね。

ここで大いなるお詫びと、胸を張った開き直りをしなければなりません。
12が対象なんだから、そのまま「12」と表示したいところです。
出来るんですけどね。ただものすごく少しだけ面倒くさいんです。
これがお仕事とかなら12と表示させるんですが、今回は処理の結果を確認したいだけなんです。なら21と表示されても、右から読めばいいじゃないですか。
というわけで、この場に限って「わたしはアラビア人~」と言い聞かせながら、右から読むことにします。

さて、元の問題に戻ります。ここでちょっとズルをします。1~100までを足すと答えは5050になります。つまり4桁です。4回処理をすればいいわけです。

; boot.asm
;
mov ax, 0x07c0
mov ds, ax

mov ah, 0x0
mov al, 0x3
int 0x10

mov cx, 12

mov dx, 0
mov ax, cx
mov bx, 10
div bx
mov cx, ax
mov ah, 0x0e
mov al, dl
add al, 0x30
int 0x10

mov dx, 0
mov ax, cx
mov bx, 10
div bx
mov cx, ax
mov ah, 0x0e
mov al, dl
add al, 0x30
int 0x10

; end of proccess
;
hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

PlayWithBoot.4.4

ハイライトは1回分にしか入れてません。同じ処理の繰り返しだし。
で、実行するとこうなります。

無事に0505が表示されました。右から見て5050です。

さて、このソースもソフトウェアを作成する立場としては、ちょっと看過できないです。ほぼ同じコードがドカンドカンと4回もあります。繰り返しなのだからループにしちゃいましょう。
12は2桁なので2回、5050は4桁なので4回実行しています。どんな数値にも対応するためには繰り返しを終わらせる条件がわからなければいけません。
これも中学入試の点取らせ問題レベルかなぁ。
答えが0になったらどんな数で割っても余りは出ません。というわけで答えが0になったら終了すればいいわけです。

; boot.asm
;
mov ax, 0x07c0
mov ds, ax

mov ah, 0x0
mov al, 0x3
int 0x10

mov ax, 0
mov bx, 0

._loop

add bx, 1
add ax, bx

cmp bx, 100
jl ._loop

mov cx, ax

mov dx, 0
mov ax, cx
mov bx, 10
div bx
mov cx, ax
mov ah, 0x0e
mov al, dl
add al, 0x30
int 0x10

mov dx, 0
mov ax, cx
mov bx, 10
div bx
mov cx, ax
mov ah, 0x0e
mov al, dl
add al, 0x30
int 0x10

mov dx, 0
mov ax, cx
mov bx, 10
div bx
mov cx, ax
mov ah, 0x0e
mov al, dl
add al, 0x30
int 0x10

mov dx, 0
mov ax, cx
mov bx, 10
div bx
mov cx, ax
mov ah, 0x0e
mov al, dl
add al, 0x30
int 0x10

; end of proccess
;
hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

PlayWithBoot.4.5

ハイライトは100まで足す部分と、1桁表示するところです。表示は繰り返しなので1回だけで。
実行するとこんな感じです。

今回はここまでです。
お疲れさまでした。

《2024/5/8 6:47:24》

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です