先週はさまざまなCPUのレジスタアーキテクチャ(Register architecture) について学んだ。
例えば Motolora MC6809ならば、
ここで勉強するコンパイラは Micro-C と呼ばれるもので、Cを読み 込んで、MC6809のアセンブラ(assembler)を出力する。最近のコン パイラは機械語を直接生成せずにアセンブラを出力するものが多い。 これは、少しでもコンパイラの機種依存成を小さくするためだと考 えられる。
これをAIX上で動作させるためにはMC6809のエミュレータを使う。エミュレータ は、もっとも簡単なインタプリタということもできる。ここで使うエミュレータの 構造を見て見よう。
6809 Simulator V09, By L.C. Benschop, Eidnhoven The Netherlands. /usr/open/lectures/kono/compiler/util09/v09.cは、非常に簡単な構造をしている。
このエミュレータでは、
/* 6809 registers */ Byte ccreg,dpreg; Word xreg,yreg,ureg,sreg,ureg,pcreg; Byte d_reg[2]; Word *dreg=(Word *)d_reg; /* 6809 memory space */ static Byte mem[65536];という形で、MC6809のレジスタをCのプログラムとして持っている。
main()では、以下のようにMC6809の命令を実行(execute)する。
for(;;){ ireg=mem[pcreg++]; /* Fetech 1 instruction */ (*instrtable[ireg])(); /* call a procedure for the instruction */ }(実際のプログラムではtraceなどが入っているので、もっと複雑) instrtable には、Cのfunctionが一覧表になっている。
int (*instrtable[])() = { neg , ill , ill , com , lsr , ill , ror , asr , asl , rol , dec , ill , inc , tst , jmp , clr , flag0 , flag1 , nop , sync , ill , ill , lbra , lbsr , ill , daa , orcc , ill , andcc , sex , exg , tfr , bra , brn , bhi , bls , bcc , bcs , bne , beq , bvc , bvs , bpl , bmi , bge , blt , bgt , ble , leax , leay , leas , leau , pshs , puls , pshu , pulu , ill , rts , abx , rti , cwai , mul , ill , swi , neg , ill , ill , com , lsr , ill , ror , asr ,これは表の一部である。同じ命令(instruction)があるのは、たま たま同じcodeが同じ命令だったり、その関数の中でさらに細かく処理が 分かれたりする。
例えば、neg は、2の補数(2's complement)として 符号を変える演算だが、(nega だったら a = -a )
neg() { Byte *ea; Word a,r; a=0; ea=eaddr0(); a=*ea; r=-a; SETSTATUS(0,a,r) *ea=r; }となっている。ここで、eaddr0() は、オペランド(operand)を計算する 関数(function)である。これは、
Byte * eaddr0() /* effective address for NEG..JMP as byte poitner */ { switch( (ireg & 0x70) >> 4) { case 0: return mem+zeropage(); case 1:case 2:case 3: return 0; /*canthappen*/ case 4: return areg; case 5: return breg; case 6: return mem+postbyte(); case 7: return mem+direct(); } }などとなっている。MC6809には豊富なアドレッシングモード(addressing mode) があり、zeropage, postbye, direct などがそれを示している。
例えば、
NEGA レジスタAのnegativeを取る (inherent/areg,breg) NEG <$80 Direct page $80のnegative を取る (zeropage) NEG $8000 Meomry $8000 のnegative を取る (direct) NEG ,X Index register X の指すmemoryのnegativeを取る (index/postbyte)Indexにはpostbyteで指定されるのだが、さらにいろいろなものを使うことが できる。
NEG 5,X X + 5 が指すmemoryのnegativeを取る NEG ,X+ X が指すmemoryのnegativeを取る、そして X を一つ進める NEG ,-X Xを一つ減らしてkら、X が指すmemoryのnegativeを取る NEG A,X X + A が指すmemoryのnegativeを取る NEG D,X X + D が指すmemoryのnegativeを取る NEG [5,X] X + 5 が指すmemoryの中身が指すmemory のnegativeを取るpostbyteの処理は少し複雑なので、ここでははぶこう。良く使う ,X や、5,X などは短くcodingされ、1000,Xなどは長くcodingされるように工夫されている。
MC6809はbig-endianであり、メモリにはメモリのアドレス が小さい方に、 数値の大きい桁から格納される。また、16bit Dレジスタは、 高い方の桁が8bit A レジスタ、低い方の桁が8bit B レジスタとなっている。 以下の数値は16進表記とする。
メモリの内容が以下のようになっているとき、
0100: 23 0101: 45 0102: 56 0103: 78 5678: 01 5679: 02 567a: 03 567b: 04 7856: 05 7857: 06 7858: 07 7859: 08
MC6809では、基本的に演算は、register to memory で行われる。 ここでは、以下のプログラムを使おう。
int a,b; main() { a = a+1-(b-123); return a; }これを /usr/open/lectures/kono/compiler/mc/mc でコンパイルしてみると、
a EQU 0 b EQU 2 main PSHS U # function call LEAU ,S # local variable の場所を取る LDD 2,Y # b を D に入れる SUBD #123 # それから 123 を引く(sub) PSHS D # D をstackに積む LDD 0,Y # a を D に入れる ADDD #1 # それに 1 を足す(add) SUBD ,S++ # それからstackにつんだものを引く STD 0,Y # それをaに入れる LDD 0,Y # a の値を main() の値として返す。 PULS U,PC _1 RTS _INITIALIZE EQU _1 _GLOBALS EQU 4 ENDとなる。Sがstackであり、Yが大域変数を指し示している。
mc と v09 を使って、上のプログラムをtraceして見よ。
より複雑なプログラムの場合はどうなるか調べて見よう。かけ算等は、 関数呼び出しにコンパイルされるので、足し算引き算ぐらいを実行する のが良い。