シミュレータ
2007年6月17日
新しいニーモニック表にしたがって作成した、シミュレータ。正確には、このシミュレータを書いていく上で、新しいニーモニック表が出来上がったのだけれど。
main.cpp
いよいよ、クロックのタイミングにあわせたコードにすることになった。2つのクロックの動きにより、4つのイベントを実行する。なお、clock2' は、clock1が立ち上がるときのclock2の値を記憶して作成される。
b2レジスタのinc, dec ルーチンは、main() 関数に移動している。
このあたりが、新しい仕様にしたがってニーモニック表を書き換えた部分。selector()関数にalternativeフラグを導入し(下記参照)、レジスタの選択を完結にした。
if 文は、フラグの値にしたがって、ip レジスタをインクリメントし、命令を一つ省略する。アセンブラでこういう仕様は見たことがないが、ニーモニック表をすべて1バイトにそろえたので、こういった処理が可能になった。jmp文だけを制御する通常のアセンブラよりも、柔軟性が出るだろう。
引数の3は、マスクに用いる値(1、3、または7)。1は、alternativeモードを利用することを示している。なお、0x44 は、『add a2, a1』に相当するが、『add a1,a2』と同じなので、これを省いて nop 命令にした。
inc/dec 命令も、3でマスクしてレジスタを選択している。下から3ビットめ(&4で得られる)は、inc か dec かを選択。
シフト命令にも、selector()によるレジスタ選択を利用してみた(1でマスク)。そのほかの4つの命令は、個々に実行回路を選択する必要がありそう。
Xレジスタへの代入も、alternative モードを利用することで、簡潔に書ける。
selector()関数は、以前と比べて順序が変わっている。また、alternative フラグがセットされていると、選択するレジスタが一部変更されるようになっている。
ここは、変化なし。
register.cpp
以前との違いは、alternative モードが追加されたこと。
math.cpp
新しく加わった、算術演算のための関数郡。特にメモすることはなさそう。
いよいよこのシミュレータを起動するときが来たようだ。それには、マシン後で書かれたプログラムを、メモリクラスオブジェクトに挿入しなければならない。この操作は、ROMに相当する部分。
<%media(20070617-simulator.zip|simulator.zip)%>
main.cpp
#include <stdlib.h> #include <stdio.h> #include <conio.h> #include "simulator.h" #include "memory.cpp" MEMORY memory; unsigned char command; #include "register.cpp" REGISTER_X x; REGISTER_A a1; REGISTER_A a2; REGISTER_S ds; REGISTER_S ss; REGISTER_S cs; REGISTER_B b1=new REGISTER_B(&ds); REGISTER_B b2=new REGISTER_B(&ss); REGISTER_P ip=new REGISTER_P(&cs); REGISTER_F f; #include "math.cpp" /* Timing chart clock1 clock2 clock2' 0 1 0 1 0 1 | | | +--+ | +--+ event 1 | | | +--+ +--+ | event 2 | | | +--+ | +--+ event 3 | | | +--+ +--+ | event 4 | | | */ int main (){ REGISTER* r; ip.reset(); f.reset(); while(1){ // Event 1 // Prepare environment for reading command // Decrement b2 register for PUSH r=&ip; if ((command & 0x10) && !(command & 0x08)) b2.dec(); // Event 2 // Read command from memory command=r->getValue(); // Event 3 // Prepare environment for command // increment ip // Increment b2 register for POP if ((command & 0x10) && (command & 0x08)) b2.inc(); ip.inc(); // Event 4 mnemonic(); } return 0; }
いよいよ、クロックのタイミングにあわせたコードにすることになった。2つのクロックの動きにより、4つのイベントを実行する。なお、clock2' は、clock1が立ち上がるときのclock2の値を記憶して作成される。
void mnemonic(){ switch(command){ case 0x00:// mov x,a1 case 0x01:// mov x,a2 case 0x02:// mov x,b1 case 0x03:// mov x,b2 case 0x04:// mov x,ip case 0x05:// mov x,ds case 0x06:// mov x,ss case 0x07:// mov x,cs x.setValue(getData(7)); return; case 0x08:// mov a1,x case 0x09:// mov a2,x case 0x0a:// mov b1,x case 0x0b:// mov b2,x case 0x0c:// mov ip,x (jmp) case 0x0d:// mov ds,x case 0x0e:// mov ss,x case 0x0f:// mov cs,x setData(7,x.getValue()); return; case 0x10:// push a1 case 0x11:// push a2 case 0x12:// push b1 case 0x13:// push b2 case 0x14:// push ip case 0x15:// push ds case 0x16:// push ss case 0x17:// push cs b2.write(getData(7)); return; case 0x18:// pop a1 case 0x19:// pop a2 case 0x1a:// pop b1 case 0x1b:// pop b2 case 0x1c:// pop ip (ret) case 0x1d:// pop ds case 0x1e:// pop ss case 0x1f:// pop cs setData(7,b2.read()); return;
b2レジスタのinc, dec ルーチンは、main() 関数に移動している。
case 0x20:// mov xl,a1l case 0x21:// mov xl,a2l case 0x22:// mov xl,b1l case 0x23:// mov xl,b2l case 0x24:// mov xl,fl // alternative mode ( see selector() ) case 0x25:// mov xl,xh // alternative mode ( see REGISTOR_X::getValue() ) x.setLowValue(getData(7,command & 4)); return; case 0x26:// mov x,[b1] // alternative mode ( see selector() ) case 0x27:// mov x,[b2] // alternative mode ( see selector() ) x.setValue(getData(3,command & 4)); return; case 0x28:// mov a1l,xl case 0x29:// mov a2l,xl case 0x2a:// mov b1l,xl case 0x2b:// mov b2l,xl case 0x2c:// mov fl,xl // alternative mode ( see selector() ) case 0x2d:// mov xh,xl // alternative mode ( see REGISTOR_X::setLowValue() ) setLowData(7,x.getValue(),command & 4); return; case 0x2e:// mov [b1],x // alternative mode ( see selector() ) case 0x2f:// mov [b2],x // alternative mode ( see selector() ) setData(3,x.getValue(),command & 4); return;
このあたりが、新しい仕様にしたがってニーモニック表を書き換えた部分。selector()関数にalternativeフラグを導入し(下記参照)、レジスタの選択を完結にした。
case 0x30:// if z if (!ifZ()) ip.inc(); return; case 0x31:// if nz if (ifZ()) ip.inc(); return; case 0x32:// if c if (!ifC()) ip.inc(); return; case 0x33:// if nc if (ifC()) ip.inc(); return;
if 文は、フラグの値にしたがって、ip レジスタをインクリメントし、命令を一つ省略する。アセンブラでこういう仕様は見たことがないが、ニーモニック表をすべて1バイトにそろえたので、こういった処理が可能になった。jmp文だけを制御する通常のアセンブラよりも、柔軟性が出るだろう。
case 0x44:// nop return; case 0x40:// add a1,a1 case 0x41:// add a1,a2 case 0x42:// add a1,[b1] case 0x43:// add a1,[b2] case 0x45:// add a2,a2 case 0x46:// add a2,[b1] case 0x47:// add a2,[b2] x.setValue(add(a1.getValue(),getData(3,1))); return; case 0x48:// sub a1,a1 case 0x49:// sub a1,a2 case 0x4a:// sub a1,[b1] case 0x4b:// sub a1,[b2] case 0x4c:// sub a2,a1 case 0x4d:// sub a2,a2 case 0x4e:// sub a2,[b1] case 0x4f:// sub a2,[b2] x.setValue(sub(a1.getValue(),getData(3,1))); return;
引数の3は、マスクに用いる値(1、3、または7)。1は、alternativeモードを利用することを示している。なお、0x44 は、『add a2, a1』に相当するが、『add a1,a2』と同じなので、これを省いて nop 命令にした。
case 0x50:// inc a1 case 0x51:// inc a2 case 0x52:// inc ds:b1 case 0x53:// inc ss:b2 case 0x54:// dec a1 case 0x55:// dec a2 case 0x56:// dec ds:b1 case 0x57:// dec ss:b2 incOrDec(command & 3,command & 4); return;
inc/dec 命令も、3でマスクしてレジスタを選択している。下から3ビットめ(&4で得られる)は、inc か dec かを選択。
case 0x58:// shl a1 case 0x59:// shl a2 case 0x5a:// shr a1 case 0x5b:// shr a2 shift(command & 1,command & 2); return; case 0x5c:// nor a1,a2 x.setValue((a1.getValue() | a1.getValue())^0xff); return; case 0x5d:// nand a1,a2 x.setValue((a1.getValue() & a1.getValue())^0xff); return; case 0x5e:// inc x x.inc(); return; case 0x5f:// dec x x.dec(); return;
シフト命令にも、selector()によるレジスタ選択を利用してみた(1でマスク)。そのほかの4つの命令は、個々に実行回路を選択する必要がありそう。
case 0x60:// mov xl,0h case 0x61:// mov xl,1h case 0x62:// mov xl,2h case 0x63:// mov xl,3h case 0x64:// mov xl,4h case 0x65:// mov xl,5h case 0x66:// mov xl,6h case 0x67:// mov xl,7h case 0x68:// mov xl,8h case 0x69:// mov xl,9h case 0x6a:// mov xl,ah case 0x6b:// mov xl,bh case 0x6c:// mov xl,ch case 0x6d:// mov xl,dh case 0x6e:// mov xl,eh case 0x6f:// mov xl,fh case 0x70:// mov xh,0h case 0x71:// mov xh,1h case 0x72:// mov xh,2h case 0x73:// mov xh,3h case 0x74:// mov xh,4h case 0x75:// mov xh,5h case 0x76:// mov xh,6h case 0x77:// mov xh,7h case 0x78:// mov xh,8h case 0x79:// mov xh,9h case 0x7a:// mov xh,ah case 0x7b:// mov xh,bh case 0x7c:// mov xh,ch case 0x7d:// mov xh,dh case 0x7e:// mov xh,eh case 0x7f:// mov xh,fh x.setLowValue(command & 0x0f,command & 1); return; default: return; } }
Xレジスタへの代入も、alternative モードを利用することで、簡潔に書ける。
/* General data read/write codes follow */ REGISTER* selector(unsigned char mask, char alternative /* = 0 */){ switch(command & mask){ case 0x00:// a1 return &a1; case 0x01:// a2 return &a2; case 0x02:// b1 if (alternative) return b1.data(); return &b1; case 0x03:// b2 if (alternative) return b2.data(); return &b2; case 0x04:// ip if (alternative) return &f; return &ip; case 0x05:// ds if (alternative) return &x; return &ds; case 0x06:// ss return &ss; case 0x07:// cs default: return &cs; } } unsigned char getData(unsigned char mask, char alternative /* =0 */){ REGISTER* r=selector(mask,alternative); return r->getValue(alternative); } void setData(unsigned char mask, unsigned char data, char alternative /* =0 */){ REGISTER* r=selector(mask,alternative); r->setValue(data); } void setLowData(unsigned char mask, unsigned char data, char alternative /* =0 */){ REGISTER* r=selector(mask,alternative); r->setLowValue(data,alternative); }
selector()関数は、以前と比べて順序が変わっている。また、alternative フラグがセットされていると、選択するレジスタが一部変更されるようになっている。
/* flag codes follow */ void setZ(){ f.value=f.value | 1; } void resetZ(){ f.value=f.value & 0xfe; } char ifZ(){ if (f.value & 1) return 1; return 0; } void setC(){ f.value=f.value | 2; } void resetC(){ f.value=f.value & 0xfd; } char ifC(){ if (f.value & 2) return 1; return 0; }
ここは、変化なし。
register.cpp
/* begin REGISTER class */ REGISTER::REGISTER(){ value=0; } void REGISTER::setValue(unsigned char data){ value=data; } unsigned char REGISTER::getValue(char alternative=0){ return value; } void REGISTER::setLowValue(unsigned char data, char alternative=0){ value=(value & 0xf0) | (data & 15); } void REGISTER::inc(char flag=1){ value++; if (value==0 && flag) { setZ(); setC(); } else if (flag) { resetZ(); resetC(); } } void REGISTER::dec(){ if (value==0) setC(); else resetC(); value--; if (value==0) setZ(); else resetZ(); } /* end REGISTER class */ class REGISTER_X : public REGISTER{ public: void setLowValue(unsigned char data, char alternative=0){ if (alternative) value=(value & 15) | ( (data & 15)*16 ); else value=(value & 0xf0) | (data & 15); } unsigned char getValue(char alternative=0){ if (alternative) return value/16; else return value; } }; class REGISTER_A : public REGISTER{ public: void shl(){ if (value & 0x80) setC(); else resetC(); value=value<<1; if (value) resetZ(); setZ(); } void shr(){ if (value & 1) setC(); else resetC(); value=value>>1; if (value) resetZ(); setZ(); } }; class REGISTER_B : public REGISTER{ private: REGISTER* segment; REGISTER _memory; public: REGISTER_B(REGISTER* seg){ segment=seg; } REGISTER* data(){ return &_memory; } void inc(char flag=1){ value++; if (value==0) segment->inc(); } void write(unsigned char data){ memory.setSegment(segment->getValue()); memory.setAddr(value); memory.write(data); } unsigned char read(){ memory.setSegment(segment->getValue()); memory.setAddr(value); return memory.read(); } }; class REGISTER_S : public REGISTER{ }; class REGISTER_P : public REGISTER{ private: REGISTER* segment; public: REGISTER_P(REGISTER* seg){ segment=seg; } void inc(char flag=1){ value++; if (value==0) segment->inc( 0 ); // Flag shouldn't change when the ip increment. } unsigned char read(){ memory.setSegment(segment->getValue()); memory.setAddr(value); return memory.read(); } void reset(){ value=0; segment->value=0; } }; class REGISTER_F : public REGISTER{ public: void reset(){ value=0; } };
以前との違いは、alternative モードが追加されたこと。
math.cpp
unsigned char add(unsigned char var1, unsigned char var2){ unsigned short result; result=(unsigned short)var1+(unsigned short)var2; if (result & 0xff00) setC(); else resetC(); result=result & 0xff; if (result) resetZ(); else setZ(); return (unsigned char)result; } unsigned char sub(unsigned char var1, unsigned char var2){ unsigned short result; result=(unsigned short)var1-(unsigned short)var2; if (result & 0xff00) setC(); else resetC(); result=result & 0xff; if (result) resetZ(); else setZ(); return (unsigned char)result; } void incOrDec(unsigned char mask, unsigned char mode){ REGISTER* r=selector(mask); if (!mode) r->inc(); else r->dec(); } void shift(unsigned char mask, unsigned char mode){ REGISTER_A* r=(REGISTER_A*)selector(mask); if (!mode) r->shl(); else r->shr(); }
新しく加わった、算術演算のための関数郡。特にメモすることはなさそう。
いよいよこのシミュレータを起動するときが来たようだ。それには、マシン後で書かれたプログラムを、メモリクラスオブジェクトに挿入しなければならない。この操作は、ROMに相当する部分。
<%media(20070617-simulator.zip|simulator.zip)%>