AKI-H8/3048のモニタプログラム,クロスアセンブラ環境での
アセンブリ言語プログラミング
Copyright(C)29Dec2010
since 1Aug2003
coskx
1.はじめに
この文書は,AKI-H8におけるH8/3048のモニタプログラム,PCターミナルソフトでの学習を記述しています。
モ
ニタプログラムは,新しいプログラムの動作を検証する場合や,アセンブリ言語の学習者が,1命令ずつ機械語命令を実行してCPUのふるまいを観察するプロ
グラムです。モニタプログラムはROM(フラッシュメモリ)上に配置されており,検証対象のプログラムはRAM上に置かれます。
この文書では
PC上のエディタでアセンブリ言語プログラムを作成し,アセンブラに機械語プログラムに変換させた後,ターミナル画面から機械語プログラムをマイクロコンピュータに転送の後,モニタ上でこのプログラムを動作させる方法
を用いて,機械語プログラムの動作を解説します。
(本校)校内からは授業に使うため準備用ファイルが入手できます。またマニュアルもダウンロードしておくとよい。
このファイルを解凍すると「trial4」のフォルダが出てくるので, 作業用フォルダとして使います。 ((本校)LANからのみ試用のためアクセスできます。) (4J電子計算機IIの授業ではこれを使います) |
H8機械語命令ニーモニクの説明マニュアル ((本校)LANからのみアクセスできます。) |
注意
この文書で扱っているAsm2Mon.cmdはWindows2000,WindowsXP,WindowsVista,Windows7で使用可能です。
2.アセンブリ言語プログラムをエディタで作成し,アセンブルを行い,モニタ上で実行
モニタプログラム上で1行ずつ命令を直打ちしてアッセンブルできるとは言っても,そのまま数十行のプログラムを書くことは現実的ではない。
そ
こで,アセンブリ言語で書かれたソースプログラムを,汎用エディタ(たとえばTeraPad)で作成し,アセンブルし,リンク,コンバート(モトローラS
形式にするとフラッシュメモリ書き込み可能となる)して,.MOTファイルを作って,「4」と同様に1ステップずつ実行してみる。
ただし,通常はROM領域に配置されるプログラムを,RAM領域に置く。
(1)アセンブリ言語で書かれたプログラム例
プログラムは次のようなものである。
スタックポインタを初期化する(使わないのでしなくてもよい)
FF800番地の2byteデータをレジスタR0に載せる
FF802番地の2byteデータをレジスタR1に載せる
R0とR1の値を加えてR0にしまう
R0のデータをFF804番地にしまう
ただしプログラム起動の時点で,
FF800番地にはx1234,FF802番地にはx2345,FF804番地にはx3456が入っているものとする。
test.src | |
.CPU
300HA |
|
H8命令ニーモニクの説明マニュアル 学内高速読み取りバッファより
(2)アセンブルからコンバートまで
「test.src」を「Asm2Mon.cmd」にドラッグ&ドロップします。フラッシュメモリ書き込み形式ファイル「test.MOT」ができます。
この「Asm2Mon.cmd」を使用するとソースリストファイル「test.LIS」とマップファイル「test.MAP」も出来ます。
test.LIS FF130〜FF14Aまでにプログラムがあることがわかる |
** H8S,H8/300 ASSEMBLER Ver.2.0A Evaluation
software ** 08/01/03
22:16:40
PAGE 1 |
test.MAP |
H SERIES LINKAGE EDITOR Ver. 5.3B LINK COMMAND LINE LNK test -PRINT=test H SERIES LINKAGE EDITOR Ver. 5.3B PAGE : 1 *** LINKAGE EDITOR LINK MAP LIST *** SECTION NAME START - END LENGTH UNIT NAME MODULE NAME ATTRIBUTE : DATA NOSHR A H'000FF030 - H'000FF033 H'00000004 test test * TOTAL ADDRESS * H'000FF030 - H'000FF033 H'00000004 ATTRIBUTE : CODE NOSHR P H'000FF130 - H'000FF14B H'0000001C test test * TOTAL ADDRESS * H'000FF130 - H'000FF14B H'0000001C ATTRIBUTE : DATA NOSHR D H'000FF800 - H'000FF805 H'00000006 test test * TOTAL ADDRESS * H'000FF800 - H'000FF805 H'00000006 |
(3)RAM書き込み
ここでH8CPUのRAM領域へのプログラム書き込みを行う。
この作業の前に,モニタプログラム「MONITOR3048N.MOT」の転送(ROM領域への書き込み)が行われている必要がある。
もしモニタプログラムが書き込まれていなかったら,書き込んでおく必要がある。
(一度モニタプログラムがROM領域にかきこまれたなら,RAM領域への書き込みは何回でもできる)
RAM領域への書き込みでは,モニタプログラムをRUNモードで動かしておく必要がある
ハイパーターミナルを用いた機械語プログラムの転送 | teratermを用いた機械語プログラムの転送 | ||||||||
1)AKI-H8側はモニタプログラムがRUNしている状態にしておく。 3)ハイパーターミナル上で「L」コマンドを与える。
5)書き込み終了
|
1)AKI-H8側はモニタプログラムがRUNしている状態にしておく。 3)teraterm上で「L」コマンドを与える。
4)teratermの「ファイル」メニューの 5)書き込み終了
|
(4)実行前の確認
フラッシュメモリ書き込みが終了したら,以下のようにff800番地付近にデータが,ff100番地のところにプログラムが正常にダウンロードされたことを確かめよう。
内容 | モニタ画面 |
ff800番地付近の メモリ表示 |
: d ff800
ff80f <ADDR> < D A T A > < ASCII CODE > FF800 12 34 23 45 34 56 DB 7B E2 8C E7 5B 04 80 E7 7A ".4#E4V.{...[...z" |
プログラムの確認 (ディスアセンブル) |
: da ff130
ff14a FF130 7A07000FFF00 MOV.L #H'000FFF00:32,ER7 FF136 6B20000FF800 MOV.W @H'FF800:24,R0 FF13C 6B21000FF802 MOV.W @H'FF802:24,R1 FF142 0910 ADD.W R1,R0 FF144 6BA0000FF804 MOV.W R0,@H'FF804:24 FF14A 40FE BRA FF14A:8 |
(4)実行
1ステップずつ実行します。
内容 | モニタ画面 |
レジスタの確認 | : r PC=0FF11A CCR=80:I....... SP=000FFF10 ER0=00000000 ER1=00000000 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=000FFF10 |
プログラムカウンタ (PC)の変更 *1 |
: .pc PC=0FF11A ? 0ff130 CCR=80 ? . |
レジスタの確認 | : r PC=0FF130 CCR=80:I....... SP=00FFFF10 ER0=00000000 ER1=00000000 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=00FFFF10 |
1ステップずつ 実行 |
: s PC=0FF136 CCR=80:I....... SP=000FFF00 ER0=00000000 ER1=00000000 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=000FFF00 FF130 7A07000FFF00 MOV.L #H'000FFF00:32,ER7 : s PC=0FF13C CCR=80:I....... SP=000FFF00 ER0=00001234 ER1=00000000 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=000FFF00 FF136 6B20000FF800 MOV.W @H'FF800:24,R0 : s PC=0FF142 CCR=80:I....... SP=000FFF00 ER0=00001234 ER1=00002345 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=000FFF00 FF13C 6B21000FF802 MOV.W @H'FF802:24,R1 : s PC=0FF144 CCR=80:I....... SP=000FFF00 ER0=00003579 ER1=00002345 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=000FFF00 FF142 0910 ADD.W R1,R0 : s PC=0FF14A CCR=80:I....... SP=000FFF00 ER0=00003579 ER1=00002345 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=000FFF00 FF144 6BA0000FF804 MOV.W R0,@H'FF804:24 : |
ff800番地付近の |
: d ff800
ff80f <ADDR> < D A T A > < ASCII CODE > FF800 12 34 23 45 35 79 DB 7B E2 8C E7 5B 04 80 E7 7A ".4#E5y.{...[...z" |
*1 すでにプログラムカウンタPCがFF130になっているときはこの作業は不要です。
3.プログラムのブレークポイントまでの実行
「5」のプログラムがすでに書き込まれているとして,FF14Aにブレークポインタ(ここで止まってモニタに制御を戻せというしるし)を設定し,プログラム先頭からブレークポインタまでを一気に動作させます。
(1)実行前の確認
以下のようにff800番地付近にデータが,ff130番地のところにプログラムが存在することを確かめよう。和が格納されるべきアドレスFF804に実行後に和が格納されることを確かめられるように,適当な値に変更しておくとよいでしょう。
内容 | モニタ画面 |
ff800番地付近の メモリ表示 |
: d ff800
ff80f <ADDR> < D A T A > < ASCII CODE > FF800 12 34 23 45 00 00 DB 7B E2 8C E7 5B 04 80 E7 7A ".4#E...{...[...z" |
プログラムの確認 (ディスアセンブル) |
: da ff130
ff14a FF130 7A07000FFF00 MOV.L #H'000FFF00:32,ER7 FF136 6B20000FF800 MOV.W @H'FF800:24,R0 FF13C 6B21000FF802 MOV.W @H'FF802:24,R1 FF142 0910 ADD.W R1,R0 FF144 6BA0000FF804 MOV.W R0,@H'FF804:24 FF14A 40FE BRA FF14A:8 |
(2)実行
ブレークポインタを設定して実行します。
内容 | モニタ画面 |
レジスタの確認 | : r PC=0FF11A CCR=80:I....... SP=000FFF10 ER0=00000000 ER1=00000000 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=000FFF10 |
プログラムカウンタ (PC)の変更 *1 |
: .pc PC=0FF11A ? 0ff130 CCR=80 ? . |
レジスタの確認 | : r PC=0FF130 CCR=80:I....... SP=00FFFF10 ER0=00000000 ER1=00000000 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=00FFFF10 |
ブレークポインタ の設定 |
: b ff14a |
現在のプログラム カウンタのアドレスから ブレークポインタまで 実行 |
: g Break at PC=FF14A PC=0FF14A CCR=80:I....... SP=000FFF00 ER0=00003579 ER1=00002345 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=000FFF00 |
ff800番地付近の |
: d ff800
ff80f <ADDR> < D A T A > < ASCII CODE > FF800 12 34 23 45 35 79 DB 7B E2 8C E7 5B 04 80 E7 7A ".4#E5y.{...[...z" |
*1 すでにプログラムカウンタPCがFF130になっているときはこの作業は不要です。
注意 もしブレークポインタをセットせずにgコマンドで起動すると,そのまま
BRA
FF14A:8
のところで無限ループに突入するため,モニタのコマンドを受け付けなくなってしまう。
このような場合には,H8の電源を落とすしかない。
4. メモリ領域のフィル
例題1 |
アセンブラを用いて次のプログラムを作成しなさい。 |
アセンブリ プログラム |
.CPU 300HA .SECTION
P,CODE,LOCATE=H'0FF200 .SECTION D,DATA,LOCATE=H'0FF800
;データ領域0FF800から |
RAM領域へ プログラムを 転送後の 実行例 |
: da ff200 ff219 <ADDR> <CODE> <MNEMONIC> <OPERAND> FF200 7A07000FFF00 MOV.L #H'000FFF00:32,ER7 FF206 7A01000FF800 MOV.L #H'000FF800:32,ER1 FF20C F855 MOV.B #H'55:8,R0L FF20E F040 MOV.B #H'40:8,R0H FF210 6898 MOV.B R0L,@ER1 FF212 0B71 INC.L #1,ER1 FF214 1A00 DEC.B R0H FF216 46F8 BNE FF210:8 FF218 40FE BRA FF218:8 : d ff800 ff84f <ADDR> < D A T A > < ASCII CODE > FF800 00 00 FF FF 00 00 FF FF 00 00 FF FF 00 00 FF FF "................" FF810 00 00 FB FF 00 00 DF FF 00 02 FF FF 00 00 FF FF "................" FF820 00 00 FF FF 00 00 FF FF 00 00 FF FF 01 00 FF FF "................" FF830 42 00 FF FF 02 04 FE FF 08 04 ED FF 00 00 FF FD "B..............." FF840 00 00 FF FF 00 00 FF FF 00 00 FF FF 00 00 FF FF "................" : .pc PC=0FF200 ? ff200 CCR=80 ? . : b ff218 : r PC=0FF200 CCR=80:I....... SP=00FFFF10 ER0=00000000 ER1=00000000 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=00FFFF10 : g Break at PC=FF218 PC=0FF218 CCR=84:I....Z.. SP=000FFF00 ER0=00000055 ER1=000FF840 ER2=00000000 ER3=00000000 ER4=00000000 ER5=00000000 ER6=00000000 ER7=000FFF00 : d ff800 ff84f <ADDR> < D A T A > < ASCII CODE > FF800 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 "UUUUUUUUUUUUUUUU" FF810 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 "UUUUUUUUUUUUUUUU" FF820 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 "UUUUUUUUUUUUUUUU" FF830 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 "UUUUUUUUUUUUUUUU" FF840 00 00 FF FF 00 00 FF FF 00 00 FF FF 00 00 FF FF "................" : |
参考
L001:
MOV.B r0l,@ER1 ;0x55をER1の指しているところへ
INC.L #1,ER1
DEC.B r0h
BNE L001:8 ; r0h!=0の場合はL12へジャンプラベル L001
条件ジャンプ行の1文字目から書かれる名前はラベルと呼ばれ,ジャンプ命令の行き先となる。
「BNE L001:8」直前の操作の結果が0でなかった(Not Equal to 0)らL001にジャンプ
ある条件が成り立ったらジャンプするという命令は条件ジャンプと呼ばれ,
C言語でいうところのif文の構造を作ったり,ループ構造を作ったりするのに必須の命
令である。
条件ジャンプは多くの種類があるため,命令マニュアルでは「Bcc」として一括して説
明されている。
H8命令ニーモニクの説明マニュアル 学内高速読み取りバッファより
課題1 |
テキストエディタとアセンブラを用いて次のプログラムを作成しなさい。 ヒント 8bitのレジスタの扱うことのできる値の範囲は0〜255 |
5. メモリ領域のブロック転送
課題2 |
テキストエディタとアセンブラを用いて次のプログラムを作成しなさい。 |
6. IOポート
ここでアセンブリプログラムによるIOポートへのアクセスを試みる。
IOポートのうち,LEDのつながれているP5と,8bitスイッチがつながれているP2を対象とする。
LED0,LED1はP5(正確にはP5DR)のbit0,bit1に保護抵抗を介して接続されている。
P5と呼んでいるポートの実体はメモリ空間に割り当てられているIOレジスタである。
LEDの点滅をするためには,次の2つのことが必要である。点滅対象はLED0の1つだけとする。
(1)P5DDR(Data
Direction
Register)は,P5DRの各ビットを入力(外部から0Vor5VをCPUとし
て0or1として受け取る)か出力(CPUの0or1を外部に対して0Vor5Vとして出力する)かを決
定するレジスタである。
各ビットを1にすると出力設定となり,0にすると入力設定となる。
ここではbit0を1にするとLED0への出力となる。
(2)P5DRは,CPUが書き込んだ値をそのままCPUのピンにそのまま出力する8ビットのレジスタである。
このレジスタのbit0に1を出力すると5Vが出力され点灯,0を出力すると0Vが出力され消灯する。
H8/3048のハードウェアマニュアルによれば,P5DDRはメモリ空間上の1byteのように見える。
P5DDRのアドレスは,0xFFFC8である。
同様にP5DRもメモリ空間上の1byteのように見える。
P5DRのアドレスは。0xFFFCAである。
これらのアドレスは,CPU設計時に決められているため,変更することはできない。
モニタプログラムのmコマンドで,LEDの点滅をさせてみよう。
fffc8に3を設定してP5の下位2ビットを出力に設定
その後,fffcaに01,02,03,00の順に値をするとLED0のみ点灯,LED1のみ点灯,両方点灯,両方消灯の順に推移する。
次のようにモニタプログラムでLED点灯を確認しなさい。
: m fffc8 FFFC8 FF ? 03 FFFC9 FF ? . : m fffca FFFCA F0 ? 01 FFFCB FB ? . : m fffca FFFCA F1 ? 02 FFFCB FB ? . : m fffca FFFCA F2 ? 03 FFFCB FB ? . : m fffca FFFCA F3 ? 00 FFFCB FB ? . : |
|
注意 FFFC8はwrite
onlyレジスタなので,読み出してもレジスタの内容を FFCAは下位4ビットのみ有効である。上位4ビットに値を書き込むこと |
次のプログラムは,P5DDRのbit0を出力に設定したのち,P5DRのbit0に向けて1と0を交互に出力する。
LED点滅プログラム |
.CPU 300HA P5DDR .EQU H'FFFC8 .SECTION
A,DATA,LOCATE=H'0FF030 .SECTION
P,CODE,LOCATE=H'0FF130 .END |
このプログラムを実行形式にして,モニタプログラム上に読み込み,ステップ実行を続けると
LEDの点滅が見られる。
なお,.EQUというのは,C言語の#defineの働きをしている。プログラム本体に「P5DDR」
が出てきたら,それは「H'FFFC8」としてアセンブルしなさいの意味である。
課題3 |
上記のプログラムを動かして,LED0の点滅を確かめなさい。 (mon2ex03.txt) |
内部にはプルアップコントロールといって,プルアップ抵抗を介して電源につなげることを有効にする機能がある。
P2と呼んでいるポートの実体はメモリ空間に割り当てられているIOレジスタである。
8ビットスイッチの状態を取得するには,次の3つのことが必要である。取得対象はDIPSW0の1つだけとする。
(1)P2DDR(Data
Direction
Register)と呼ばれる入出力方向を設定するレジスタのbit0に,
出力として使うという設定を行なう。このビットを0にすると入力設定となる。
(このビットを1にすると出力設定となる)
(2)P2PCR(Pullup
Control
Register)と呼ばれるプルアップ制御を設定するレジスタのbit0に,
プルアップ有効という設定を行なう。このビットを1にするとプルアップ有効設定となる。
(このビットを0にするとプルアップ無効設定となる)
(3)P2DRは,CPUのピンの状態をそのまま汎用レジスタに入力するする8ビットのレジスタである。
このレジスタのbit0に5Vを与えると1が,0Vを与えると0が入力される。
H8/3048のハードウェアマニュアルによれば,P2DDRはメモリ空間上の1byteのように見える。
P2DDRのアドレスは,0xFFFC1である。
同様にP2PCRもメモリ空間上の1byteのように見える。
P2PCRのアドレスは。0xFFFD8である。
同様にP2DRもメモリ空間上の1byteのように見える。
P2DRのアドレスは。0xFFFC3である。
これらのアドレスは,CPU設計時に決められているため,変更することはできない。
課題4 |
モニタプログラムのコマンドで,8ビットDIPスイッチのビットパタンを読み取れていることを確かめてみよう。 レポートでは,8ビットDIPスイッチの状態とfffc3の内容の対応を5例ほど示しなさい。 (mon2ex04.txt) |
次のプログラムは,8ビットスイッチのSW0のON-OFFの状態を取り込み,それに対応して,
LED0を点灯あるいは消灯させる。
8ビットスイッチの状態取得とLED制御プログラム |
.CPU 300HA P5DDR .EQU H'FFFC8 .SECTION
A,DATA,LOCATE=H'0FF030 .SECTION
P,CODE,LOCATE=H'0FF130 .END |
このプログラムを実行形式にして,モニタプログラム上に読み込み,ステップ実行を続けると
8ビットスイッチのSW0のON-OFF状態に対応したLEDの点滅が見られる。
課題5 |
上記のプログラムを動かして,8ビットスイッチのSW0の変更とLED0の点滅を確かめなさい。 (mon2ex05.txt) |
7. 乗算・除算
2進数の掛け算はつぎのようなイメージである。
例 10110×1010=1101110
10110 .
× 1010 .
10110
.
10110 .
11011100 .
シフト命令と加算命令で作ることができそうである。
この考え方をC言語で書いてみると次のようになる。
掛け算をシフトと足し算で行うCプログラム |
#include "stdio.h" int main() r0=r00; printf("r0,r1,r2= %04x, %04x,
%04x\n",r00,r10,r2); /*実行例 |
一方2進数の割り算は次のようなイメージである。
例 10110110÷1010=10010 余り 10
10010 .
1010 )10110110 .
1010 .
1011 .
1010 .
10
シフト命令と減算命令(CMP命令を用いると引くことができるかどうかわかる)
割り算をシフトと引き算で行うCプログラム |
#include "stdio.h" int main() r0=r00; printf("r00,r10,r2,r0= %04x, %04x,
%04x, %04x\n",r00,r10,r2,r0); /*実行例 1025 25 |
参考 アセンブリ言語によるいくつかの条件ジャンプの例
CMP.W #12345:16,R0 BLT ABCD:8 |
R0 less than #12345:16 なら ラベルABCD:へジャンプ |
CMP.W R5,R0 BGT LOOP2:8 |
R0 grater than R5 なら ラベルLOOP2へジャンプ |
MOV.W E0,E0 BNE ZERO:8 |
E0 Not Equal 0 なら ラベルZEROへジャンプ |
課題6 |
1.テキストエディタとアセンブラを用いて次のプログラムを作成しなさい。 3.テキストエディタとアセンブラを用いて次のプログラムを作成しなさい。 |
課題7 |
1.テキストエディタとアセンブラを用いて次のプログラムを作成しなさい。 ヒント 2つの引数はr0とr1に載せて関数を呼び出す。関数は戻り値をr0に載せて返す
|
参考 正負数掛け算プログラム
正負数掛け算のCプログラム |
#include "stdio.h" int main() r0=r00; printf("r0,r1,r2= %04x, %04x,
%04x\n",(unsigned short)r00,(unsigned short)r10,(unsigned
short)r2); /*実行結果 85 -10 -85 10 -85 -10 |
7.参考(1) アッセンブルコマンド「Asm2Mon.cmd」
Asm2Mon.cmd |
rem
------------------------------------------------------------------------ @echo off rem カレントドライブ・カレントディレクトリへ移動 rem コンパイラのパスの設定 rem 誤ったファイルがドロップされたか,単なるダブルクリックで起動した場合は何もしない :ASSEMBLE :LINK :CONVERT goto TERMINAL :NO_SOURCE_ERROR :ASM_ERROR :LINK_ERROR :TERMINAL |
8.参考(2)h8v2フォルダの構成例
h8v2の構成例 |
C:\Program
Files\h8v2 ├─BIN │ ASM38.EXE │ C38ASM.EXE │ C38CGN.EXE │ C38FRNT.EXE │ C38MID.EXE │ C38PEP.EXE │ CH38.EXE │ CNVS.EXE │ h8v2cc.cmd │ LNK.EXE │ OPT38.EXE │ OPTLNK38.EXE │ ├─INCLUDE │ 3048F.H │ 3048FONE.H │ 3048S.H │ 3664S.H │ ASSERT.H │ CTYPE.H │ ERRNO.H │ FLOAT.H │ INDIRECT.H │ LIMITS.H │ MACHINE.H │ MATH.H │ NO_FLOAT.H │ SETJMP.H │ STDARG.H │ STDDEF.H │ STDIO.H │ STDLIB.H │ STRING.H │ ├─LIB │ │ C38HA.LIB │ │ C38HAS.LIB │ │ C38HN.LIB │ │ C38HNS.LIB │ │ C38REG.LIB │ │ C38REGS.LIB │ │ C8S26A.LIB │ │ C8S26AS.LIB │ │ C8S26N.LIB │ │ C8S26NS.LIB │ │ start3048.OBJ │ │ start3048I.OBJ │ │ │ |