Open Watcom IDE -- Cからとアセンブラールーチンを呼び出す ― 2018年03月02日 17:34
Open Watcom IDEをいじるに当たり、ただ漠といじってもつまらないので、 PC-DOS用のシェルの作成を計画しました。もちろんネタ本があります。 CQ出版社から1989年7月20日に出版された「Allen Holib著 横山和由訳 MS-DOS用Shellの実現 (On Command: Writing a Unix-Like Shell for MS-DOS)」です。 本棚の肥やしになっていたのを引っ張り出してきました。この本を購入した頃は、 まだまだ、コンパイラーなどのツールが高価で、実際の制作には、踏み切れませんでした。
内容は、丁寧にシェルの実装について、ソースリストを交えて解説してあります。 特に難しい内容ではなく、容易に著者の開発手順をたどり、シェルを作成することができそうです。 原著が書かれたのは、1986年頃で、その当時書き方で、MS-C Ver.4とMASMで記述されています。 現代のコーディングスタイルと比較して、古い書き方をしてあり、私の好みに合わない書き方です。 今回は、現代風のスタイルに合わせて書いていきます。主な変更点は、以下の通りです。
- 関数の宣言で、"int"型の変数を明示的に宣言していないのを 全て宣言するようにした。
- "unsinged int"型の宣言を "unsinged"としてあるのを、"unsinged int"と記述するようにした。
- register変数は、とりあえず使わない。
- ユーティリティーライブラリーは複数のソースファイルに分かれているが、共通の インクルードファイルにまとめてプロトタイプ宣言をしている。デバックしづらいので、一つの Cソースファイルに対応して一つのヘッダーファイルを作成しプロトタイプ宣言をするようにした。
- 標準関数のプロトタイプ宣言を 直接コーディングしてあるのを ヘッダファイルをインクルード するようにした。
- Static属性の付いた関数は、そのままではデバッグしづらいので、コンパイル時に"DEBUG"を定義してないときは、 static属性をつけてコンパイルをするように"#ifndef DEBUG"を関数の宣言に追加した。
- 制作にはOpen Watcom C/C++コンパイラーとWASMを使用する。
- 開発はOS/2で行い、デバッグはOS/2のPC-DOS窓で行う。
開発はC:ドライブに"Shell"ディレクトリを作成し、その中にコードを書くこととしました。
前準備ができたので、まずは、手始めに、Cのmain()関数から、 アセンブラーで記述した関数foo()を呼び出す練習をしました。 ソースコードは、下記の通りです。
/* callasm.c */
#include
#include "foo.h"
int main() {
int p;
p = 7;
printf("%d => %d\n",p,foo(p));
return 0;
}
/* foo.h */
extern int foo(int);
; foo.asm
_text SEGMENT BYTE PUBLIC 'CODE'
_text ENDS
_data SEGMENT WORD PUBLIC 'DATA'
_data ENDS
const SEGMENT WORD PUBLIC 'CONST'
const ENDS
_bss SEGMENT WORD PUBLIC 'BSS'
_bss ENDS
dgroup GROUP const,_bss,_DATA
ASSUME CS:_text, DS:dgroup, SS:dgroup, ES:dgroup
_text SEGMENT
; ---------------------------------------------------------------------
; foo(int val)
; val=bp+4
_foo PROC NEAR
PUBLIC _foo
push bp
mov bp,sp
mov ax,[bp+4]
shl ax,1
mov sp,bp
pop bp
ret
_foo ENDP
_TEXT ENDS
END
Wasmは、MASM互換といわれているので、一発でうまくいくと思っていたのですが、だめでした。 ビルドしてみると、下記のようにリンケージでエラーが出ます。callasm.cで呼び出している関数foo()は、 コンパイルされると、"foo_"となるようです。
cd C:\SHELL\SAMPLE
wmake -f C:\SHELL\SAMPLE\callasm.mk -h -e C:\SHELL\SAMPLE\callasm.exe
wasm FOO.ASM -i="E:\WATCOM/h" -ms -fp0 -w4 -e25
Open Watcom Assembler Version 1.9
Portions Copyright (c) 1992-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See http://www.openwatcom.org/ for details.
FOO.ASM: 27 lines, 0 warnings, 0 errors
wcc callasm.c -i="E:\WATCOM/h" -w4 -e25 -zp2 -od -fp2 -fpi87 -bt=dos -fo=.obj -ms
Open Watcom C16 Optimizing Compiler Version 1.9
Portions Copyright (c) 1984-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See http://www.openwatcom.org/ for details.
callasm.c: 11 lines, included 773, 0 warnings, 0 errors
Code size: 60
wlink name callasm d all sys dos op m op maxe=25 op q op symf @callasm.lk1
Error! E2028: foo_ is an undefined reference
file callasm.obj(C:\SHELL\SAMPLE\callasm.c): undefined symbol foo_
Error(E42): Last command making (C:\SHELL\SAMPLE\callasm.exe) returned a bad status
Error(E02): Make execution terminated
Execution complete
というわけで、foo.asm内の"_foo"を"foo_"に編集し再ビルドしました。
; foo.asm
_text SEGMENT BYTE PUBLIC 'CODE'
_text ENDS
_data SEGMENT WORD PUBLIC 'DATA'
_data ENDS
const SEGMENT WORD PUBLIC 'CONST'
const ENDS
_bss SEGMENT WORD PUBLIC 'BSS'
_bss ENDS
dgroup GROUP const,_bss,_DATA
ASSUME CS:_text, DS:dgroup, SS:dgroup, ES:dgroup
_text SEGMENT
; ---------------------------------------------------------------------
; foo(int val)
; val=bp+4
foo_ PROC NEAR
PUBLIC foo_
push bp
mov bp,sp
mov ax,[bp+4]
shl ax,1
mov sp,bp
pop bp
ret
foo_ ENDP
_TEXT ENDS
END
再ビルドしてみると、エラーはなくなりました。
cd C:\SHELL\SAMPLE
wmake -f C:\SHELL\SAMPLE\callasm.mk -h -e C:\SHELL\SAMPLE\callasm.exe
wasm FOO.ASM -i="E:\WATCOM/h" -ms -fp0 -w4 -e25
Open Watcom Assembler Version 1.9
Portions Copyright (c) 1992-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See http://www.openwatcom.org/ for details.
FOO.ASM: 27 lines, 0 warnings, 0 errors
wlink name callasm d all sys dos op m op maxe=25 op q op symf @callasm.lk1
Execution complete
テストです。
C:\SHELL\SAMPLE>callasm
7 => 3522
アセンブラールーチン"foo"は、受け取った引数を左に1bitシフトする事で受け取った引数を2倍して値を返します。 結果は、"14"になるはずですが、そうなっていません。確認のため、 "callasm.c"のコンパイル結果を見るためにコンパイルリストをコンパイラーに吐かせようとしましたが、"wcc"のオプションに 該当するものが見当たらいません。調べてみると"Open Watcom Assembler"Wikiの"Disassembler"の項目に、 以下の記述を見つけました。
The assembler does not have listing facilities; instead the use of
wdis for generating listings is recommended.
この指示に従い、callasm.objを"wdis"で、逆アセンブルしてみました。
Module: C:\SHELL\SAMPLE\callasm.c
GROUP: 'DGROUP' CONST,CONST2,_DATA
Segment: _TEXT BYTE USE16 0000003C bytes
0000 main_:
0000 B8 18 00 mov ax,0x0018
0003 E8 00 00 call __STK
0006 53 push bx
0007 51 push cx
0008 52 push dx
0009 56 push si
000A 57 push di
000B 55 push bp
000C 89 E5 mov bp,sp
000E 81 EC 04 00 sub sp,0x0004
0012 C7 46 FE 07 00 mov word ptr -0x2[bp],0x0007
0017 8B 46 FE mov ax,word ptr -0x2[bp]
001A E8 00 00 call foo_
001D 50 push ax
001E FF 76 FE push word ptr -0x2[bp]
0021 B8 00 00 mov ax,offset DGROUP:L$1
0024 50 push ax
0025 E8 00 00 call printf_
0028 83 C4 06 add sp,0x0006
002B C7 46 FC 00 00 mov word ptr -0x4[bp],0x0000
0030 8B 46 FC mov ax,word ptr -0x4[bp]
0033 89 EC mov sp,bp
0035 5D pop bp
0036 5F pop di
0037 5E pop si
0038 5A pop dx
0039 59 pop cx
003A 5B pop bx
003B C3 ret
Routine Size: 60 bytes, Routine Base: _TEXT + 0000
No disassembly errors
Segment: CONST WORD USE16 0000000A bytes
0000 L$1:
0000 25 64 20 3D 3E 20 25 64 0A 00 %d => %d..
Segment: CONST2 WORD USE16 00000000 bytes
Segment: _DATA WORD USE16 00000000 bytes
これを見ると、"0017"行目から"001A"行目で、"ax"レジスタに引数の値を入れて、 "foo_"を呼び出しているのが分かります。想定外でした。関数の引数は、基本スタックに 積む物だと思っていたのですが、Open Watcom C/C++では違うようです。困りました。アセンブラールーチンを 書き換えたくはありませんので、コンパイラーのオプションスイッチで、切り抜ける方法を探ってみたところ、 該当しそうなオプションスイッチがありました。
-ecc set default calling convention to __cdecl
-ecd set default calling convention to __stdcall
-ecf set default calling convention to __fastcall
-ecp set default calling convention to __pascal
-ecr set default calling convention to __fortran
-ecs set default calling convention to __syscall
-ecw set default calling convention to __watcall (default)
OS/2のシステム関数を呼び出すときには、関数に"CDECL"とつけていたように記憶しています。"-ecc"が 怪しいです。まずは、foo.asm中の"foo_"を"_foo"に戻し、"-ecc"をつけて再ビルドしてみました。
cd C:\SHELL\SAMPLE
wmake -f C:\SHELL\SAMPLE\callasm.mk -h -e C:\SHELL\SAMPLE\callasm.exe
wasm FOO.ASM -i="E:\WATCOM/h" -ms -fp0 -w4 -e25
Open Watcom Assembler Version 1.9
Portions Copyright (c) 1992-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See http://www.openwatcom.org/ for details.
FOO.ASM: 27 lines, 0 warnings, 0 errors
wcc callasm.c -i="E:\WATCOM/h" -w4 -e25 -zp2 -ecc -od -fp2 -fpi87 -bt=dos -fo=.obj -ms
Open Watcom C16 Optimizing Compiler Version 1.9
Portions Copyright (c) 1984-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See http://www.openwatcom.org/ for details.
callasm.c: 11 lines, included 773, 0 warnings, 0 errors
Code size: 63
wlink name callasm d all sys dos op m op maxe=25 op q op symf @callasm.lk1
Execution complete
callasm.objを逆アセンブルした結果は、以下のようになり、関数のパラメーターはスタックに積んで 渡すようになりました。
Module: C:\SHELL\SAMPLE\callasm.c GROUP: 'DGROUP' CONST,CONST2,_DATA Segment: _TEXT BYTE USE16 0000003F bytes 0000 main_: 0000 B8 18 00 mov ax,0x0018 0003 E8 00 00 call __STK 0006 53 push bx 0007 51 push cx 0008 52 push dx 0009 56 push si 000A 57 push di 000B 55 push bp 000C 89 E5 mov bp,sp 000E 81 EC 04 00 sub sp,0x0004 0012 C7 46 FE 07 00 mov word ptr -0x2[bp],0x0007 0017 FF 76 FE push word ptr -0x2[bp] 001A E8 00 00 call _foo 001D 83 C4 02 add sp,0x0002 0020 50 push ax 0021 FF 76 FE push word ptr -0x2[bp] 0024 B8 00 00 mov ax,offset DGROUP:L$1 0027 50 push ax 0028 E8 00 00 call printf_ 002B 83 C4 06 add sp,0x0006 002E C7 46 FC 00 00 mov word ptr -0x4[bp],0x0000 0033 8B 46 FC mov ax,word ptr -0x4[bp] 0036 89 EC mov sp,bp 0038 5D pop bp 0039 5F pop di 003A 5E pop si 003B 5A pop dx 003C 59 pop cx 003D 5B pop bx 003E C3 ret Routine Size: 63 bytes, Routine Base: _TEXT + 0000 No disassembly errors Segment: CONST WORD USE16 0000000A bytes 0000 L$1: 0000 25 64 20 3D 3E 20 25 64 0A 00 %d => %d.. Segment: CONST2 WORD USE16 00000000 bytes Segment: _DATA WORD USE16 00000000 bytes
テストです。
C:\SHELL\SAMPLE>callasm
7 => 14
成功です。これで、Cからアセンブラールーチンを呼び出す手順を確認できました。一山、越えました。
最近のコメント