インターネット&PC120%活用:PC活用編

〜アセンブラ入門〜
アセンブラでCOMファイル作成

前回まで、DEBUGを使ってちょっと遊んでみましたが、今回はアセンブラでCOMファイルを作成
してみます。


1 アスキーコード表示プログラム
まずは、前回DEBUGで作成した”アスキーコード表示プログラム”を題材にしてみます。
ソースは以下の通りです。プログラムの流れはDEBUGで機械語プログラムその2で説明して
いますので、確認して下さい。

;アスキーコード表示 coded by Tsuyoshi Kasai     


code segment
assume cs:code,ds:code,es:code,ss:code
org 0100h

start:
    mov ah,1
    int 21h
    mov bl,al
    mov dl,' '
    call dsp
    mov cl,4
    mov dl,bl
    shr dl,cl
    call dispkeycode
    mov dl,bl
    and dl,0fh
    call dispkeycode
    mov ah,9
    mov dx,offset crlf
    int 21h
    jmp start

dispkeycode:
    cmp dl,9
    jg alpha
    add dl,30h
    jmp dsp
    alpha:
    add dl,37h
dsp:
    mov ah,2
    int 21h
    ret

crlf db 0dh,0ah,'$'

code ends
   end start

DEBUGの時と記述が少し違います。
ここでは、ちょっと古いアセンブラソースの書き方をしました。(今はもっ簡略化した記述の仕方もある)

(1) SEGMENT擬似命令 (segment   ends)
コードおよびデータの属するセグメントを定義します。
セグメント名 segment
     [Program本体]
セグメント名 ends

というように記述します。

(2) ASSUME
CS,DS,ES,SSなどのセグメントレジスタに、SEGMENTで定義したセグメント名を指定します。
これでセグメントレジスタにセグメントアドレスが設定されます。
COMファイルは64Kbyte以内の小さな実行ファイルで、コードもデータもすべて同じセグメント内に
あります。従って、ここでは全てのセグメントレジスタに同じセグメントを設定します。(ここではcodeという
セグメント名を設定)

(3) ORG
オフセットアドレスを指定します。
COMファイルは100H番地からコードが始まるので org 100h とします。

(4) END
ソースの終わりに記述します
end プログラム開始アドレス(ラベル)
というように記述します。

(5) ラベル
DEBUGでは call や jump 命令の飛び先をアドレスで記述しましたが、アセンブラソースでは
ラベルを飛び先の指定に使います。
このプログラムでは、
start:   dispkeycode:  dsp:
というのがラベルです。

(6) 変数(データラベル)とOFFSET
データを示すのが変数(データラベル)です。ここでは crlf がそうです。
また、変数の値そのものではなく、変数の格納されているアドレスを参照する時は
offset 変数名
と記述します。

(7) 数値表現
DEBUGでは数値は全て16進数でしたが、アセンブラでは、通常の数値は10進数とみなされ
16進数には h をつけます。
また ' ' で囲むと文字をデータとして記述できます。文字はキャラクタコードに変換されてアセンブル
されます。
CMP AL, 'A' は CMP AL, 41h と同じことになります。

ごく簡単にですが、DEBUGとの記述の違いについて説明してみました。
アセンブラソースの書き方がだいたい分かっていただけたと思います。

(8) アセンブル、リンク、COMファイル作成
最初なので、アセンブルからCOMファイル作成までの手順をのせておきます。
アセンブラはMASM32(ml.exe)を使用しました。

D:\MASM32\BIN>ml /c /Fl dispkeycode.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: dispkeycode.asm

D:\MASM32\BIN>dir dispkeycode.*

 ドライブ D: のボリュームラベルはありません.
 ボリュームシリアル番号は 1F03-2A76
 ディレクトリは D:\MASM32\BIN

DISPKE~1 ASM           569  00-12-21  21:56 dispkeycode.asm
DISPKE~1 OBJ           117  00-12-21  21:56 dispkeycode.obj
DISPKE~1 LST         1,784  00-12-21  21:56 dispkeycode.lst
         3 個               2,470 バイトのファイルがあります
         0 ディレクトリ    156,540,928 バイトの空きがあります


C:\newgame\ASM>link dispke~1.obj

Microsoft 8086 Object Linker
Version 3.00 (C) Copyright Microsoft Corp 1983, 1984, 1985

Run File [DISPKE~1.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
Warning: no stack segment


C:\newgame\ASM>exe2bin dispke~1.exe dspkeycd.com

C:\newgame\ASM>dspkeycd
1 31
2 32
3 33
! 21
v 76
  20

C:\newgame\ASM>debug dspkeycd.com
-u
25CD:0100 B401          MOV     AH,01
25CD:0102 CD21          INT     21
25CD:0104 8AD8          MOV     BL,AL
25CD:0106 B220          MOV     DL,20
25CD:0108 E82700        CALL    0132
25CD:010B B104          MOV     CL,04
25CD:010D 8AD3          MOV     DL,BL
25CD:010F D2EA          SHR     DL,CL
25CD:0111 E81100        CALL    0125
25CD:0114 8AD3          MOV     DL,BL
25CD:0116 80E20F        AND     DL,0F
25CD:0119 E80900        CALL    0125
25CD:011C B409          MOV     AH,09
25CD:011E BA3701        MOV     DX,0137
-u
25CD:0121 CD21          INT     21
25CD:0123 EBDB          JMP     0100
25CD:0125 80FA09        CMP     DL,09
25CD:0128 7F05          JG      012F
25CD:012A 80C230        ADD     DL,30
25CD:012D EB03          JMP     0132
25CD:012F 80C237        ADD     DL,37
25CD:0132 B402          MOV     AH,02
25CD:0134 CD21          INT     21
25CD:0136 C3            RET
25CD:0137 0D0A24        OR      AX,240A
25CD:013A 243D          AND     AL,3D
25CD:013C FFFF          ???     DI
25CD:013E 7502          JNZ     0142
25CD:0140 EB85          JMP     00C7

/cはリンクさせないオプションスイッチ
/Flはリストファイルを生成


アセンブル完了







DOS窓ではlongファイル名がサポート
されないので、ファイル名が短縮形に
なってしまっている




リンカはCドライブにあるので、以後C
ドライブで作業
16bitリンカでリンク




Stack segmentがないという警告がでるがCOMファイルにするので、無視して良い
これで、exeファイルができた
COMファイルに変換
dskkeycd.comというファイル名にした
実行結果







DEBUGで逆アセンブルしてみた















 

2 2進数変換プログラム

(1) プログラム作成
ついでに、簡単なプログラムとして10進数−>2進数変換プログラムをアセンブラで作成してみます
10進数を入力すると2進数に変換して表示します。ただし、このプログラムは1Byteしかデータを
扱わないので、255以下の数しか正確に扱えません。また、終了は[Ctrl]+[c]です。

簡単にプログラム全体を概観して説明しておきます。
下の実行例を見ていただけばわかると思いますが、2と入力すると、00000010と2進数の結果を返す
プログラムです。

変数(データラベル)は以下の3つです。
crlfdata  改行用のデータ 
buf  入力されたデータ用。最初の1byteめに入力されたデータのバイト数が格納され、2byteめ以降に
    実際に入力されたデータが格納されます。(データはキャラクタコードが格納される)
result 入力されたデータをバイナリー値として保持するための変数。

例えば、12 と入力すると bufには、2, 31h(1のキャラクタコード), 32h(2のキャラクタコード) が格納されます。

12を入力した場合
buf[1] buf[2] buf[3]
2 31h 32h

これをバイナリー値に変換するとresultには、0Ch(=12)が格納されます。
このキャラクタコード−>バイアナリー値変換を行うのがcalcルーチンです。

このプログラムのもうひとつの重要なルーチンが、resultに保持されたバイナリー値を2進数に変換して
ディスプレイに表示するルーチンで、主にshiftというルーチンで処理を行います。
以下、実際のコードを掲載しますので見て下さい。コードの後に、プログラムの流れを説明します。

;2進数表示  coded by Tsuyoshi Kasai


code segment
assume cs:code,ds:code,es:code,ss:code
org 0100h

start:
      mov result,0
      mov ah,0ah
      mov dx,offset buf
      int 21h
      call crlf
      mov cl,1
      mov bl,buf[1]
      mov bh,0
L1:
     mov al,buf[bx+1]   ;@
     call calc
     dec bl
     jz disp
     mov al,cl
     mov ch,0ah
     mul ch
     mov cl,al
     jmp L1

disp:
     mov dx,result
     mov bl,dl 
     mov ch,8

shift:
     and dl,80h       ;A
     mov cl,7         ;B
     shr dl,cl         
     add dl,30h       ;C           
     mov ah,2         
     int 21h          
     dec ch
     jz L2
     shl bl,1         ;D    
     mov dl,bl
     jmp shift
L2:
     call crlf
     jmp start

calc:
     sub al,30h
     mul cl
     add result,ax
     ret

crlf:
     mov ah,9
     mov dx, offset crlfdata
     int 21h
     ret

crlfdata db 0dh,0ah,'$'
buf db 4,0,0,0,0,0
result dw 0
  
code ends
     END start
      







resultの初期化。resultは入力された数値をバイナリー値に変換して保持するための変数
数値の入力(キーボードから) システムコール10番
dxに最終的に入力された値が格納される。

改行ルーチンコール
clは1の位、10の位、100の位を表すのに使用
buf[1]には入力されたデータのデータバイト数が入る。
blに入力データバイト数をセット、bhに0をセット
結局bxに入力データバイト数がセットされる。
入力された数値(キャラクタコード)をalセットしCALCを
コール。CALCはキャラクタコードを実際の数値に変換
入力データを全て処理したら(bl=0)表示ルーチンdisp:をコール
入力データ残っていたら、10の位−>100の位と処理。
al <-cl  ch = 10
ax = al(<-cl) * ch(=10)
cl <- al これでclの値は10倍された

disp: 表示ルーチン
resultに格納された値をdxレジスタ(ここでは下位1byteしか扱わないので、実質dl)に入れる。
2進数への変換時の操作用として、bl, dl を使用。blの値をシフト途中の結果保持用に、dlをキャラクタコード変換作業用レジスタとして使用。chにシフト回数(=8)をセット
shift: 1bitづつシフトしながら、2進数に変換するルーチン
and で最上位bitだけ残して、他はマスク

7bitシフト  最上位bit −>最下位bitにシフト
30h足して、数値−>キャラクタコード(0か1)に変換
表示

8bit分終わったら(ch=0)、L2(改行)へ

blを1bit左シフト。次のbitが最上位bitに入る
CF <-- [MSB <-...- LSB] <--0





calc: キャラクタコードを実際の数値に変換する
30h引いて、キャラクタコード−>実際の数値
clには1の位の時は1、10の位では10
100の位では100が入っている
result = result + ax(=al*cl)

改行ルーチン






全体の流れを123を入力したと仮定して説明します。
1)まず、1の位の取り出し。      3をとりだします。(L1: 直後の @mov al,buf[bx+1] )
2)キャラクタコード−>数値変換   これはキャラクタコードで33hなので、calcルーチンで3に変換
3)10の位の取り出し          2を取り出し
4)キャラクタコード−>数値変換   CALCルーチンで20に変換して3と加算して23にする
5)100の位の取り出し         1を取り出し
6)キャラクタコード−>数値変換   CALCルーチンで100に変換し23と加算し、123にする
7)最上位ビット取り出し         123は7Bh(=01111011)なので0を取り出し (shift: 直後の Aand dl,80h )
8)最上位bit−>最下位bit (7bitシフト)  0(=00000000) (shift 2,3行目の Bmov cl,7 shr dl,cl )
9)表示                   キャラクタコードに変換して(30h)表示 (shift 4,5,6行目 Cadd dl,30h
                         mov ah,2 int 21h)
10)以下1bitづつシフトして7)−>9)の繰り返し (shift下の3行 Dshl bl,1 mov dl,bl jmp shift)

実行結果の例をのせておきます。

C:\newgame\ASM>dispbin       
123
01111011
11
00001011
255
11111111
0
00000000
56
00111000
^C

C:\newgame\ASM>


(2) 機械語解説
今回初登場の機械語は、乗算のMULです。8bit同士の乗算(結果は16bit)と16bit同士の乗算(結果は32bit)の
2種類の使い方があります。

ニモニック 機能 記述例
MUL 汎用レジスタ
MUL メモリー
8bit汎用レジスタ(またはメモリー)とALを乗算し、結果はAXに格納する
AX <- AL * 8bit汎用レジスタ(メモリー)
16bit汎用レジスタ(またはメモリー)とAXを乗算し、結果はDX+AXに格納される
[DX/AX] <- AX * 16bit汎用レジスタ(またはメモリー)
MUL BL

MUL BX

ではでは、今回はこれにておわり

TopPage


inserted by FC2 system