シミュレータ
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)%>