C++ 擬似コードで読む Comet2 の命令仕様
はじめに
情報処理の試験で使う Comet2 の命令仕様について、実際にどういう意味なのかを C++ 風の擬似コードを示して説明とします
アセンブラについては「C++プログラマのためのアセンブラ入門」でも説明しています
C/C++ の動作の詳細としてのアセンブラ入門は「C/C++ ポインタ入門」を見るといいでしょう
出典
IPA 情報処理推進機構
HOME >> 情報処理技術者試験 >> 試験実施案内 >> 「試験で使用する情報技術に関する用語・プログラム言語など」及び「試験要綱」改訂版の公開について
https://www.jitec.ipa.go.jp/1_02annai/annai_yogo_jis_kaitei.html
「試験で使用する情報技術に関する用語・プログラム言語など」 Ver 2.0(PDF形式)
https://www.jitec.ipa.go.jp/1_13download/shiken_yougo_ver2_0.pdf
別紙1 アセンブラ言語の仕様
1. システムCOMET IIの仕様
1.1ハードウェアの仕様
1語は 16ビットで、そのビット構成は、次のとおりである。
上位 8ビット 下位 8ビット ビット番号 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 値 ↑
符号(負: 1、非負: 0)- 主記憶の容量は 65536語で、そのアドレスは 0〜65535番地である。
- 数値は、16ビットの 2進数で表現する。
負数は、2の補数で表現する。 - 制御方式は逐次制御で、命令語は 1語長又は 2語長である。
- レジスタとして、 GR(16ビット)、SP(16ビット)、PR(16ビット)、FR(3ビット)の 4種類がある。
- GR(汎用レジスタ、General Register)は、
GR0〜GR7の 8個があり、算術、論理、比較、シフトなどの演算に用いる。
このうち、GR1〜GR7のレジスタは、指標レジスタ(index register)としてアドレスの修飾にも用いる。 - SP(スタックポインタ、Stack Pointer)は、
スタックの最上段のアドレスを保持している。 - PR(プログラムレジスタ、Program Register)は、
次に実行すべき命令語の先頭アドレスを保持している。 - FR(フラグレジスタ、Flag Register)は、
OF(Overflow Flag)、SF(Sign Flag)、ZF(Zero Flag)と呼ぶ 3個のビットからなり、演算命令などの実行によって次の値が設定される。
これらの値は、条件付き分岐命令で参照される。- OF
算術演算命令の場合は、演算結果が -32768〜 32767 に収まらなくなったとき 1になり、それ以外のとき 0になる。((( r32 & 0x8000u ) << 16 ) != ( r32 & 0x80000000u )) (( static_cast<int16_t>(r32) < 0 ) != ( r32 < 0 ))
論理演算命令の場合は、演算結果が 0 〜 65535 に収まらなくなったとき 1になり、それ以外のとき 0になる。
(( r16 & 0xffff0000u ) != 0 )
- SF
演算結果の符号が負(ビット番号 15が 1)のとき 1、それ以外のとき 0になる。( r16 < 0 )
- ZF
演算結果が零(全部のビットが 0)のとき 1、それ以外のとき 0になる。( r16 == 0 )
- OF
- GR(汎用レジスタ、General Register)は、
- 論理加算又は論理減算は、被演算データを符号のない数値とみなして、加算又は減算する。
算術計算命令は値を signed として、論理計算命令は unsigned として扱う
1.2 命令
命令の形式及びその機能を示す。
ここで、一つの命令コードに対し 2種類のオペランドがある場合、上段はレジスタ間の命令、下段はレジスタと主記憶間の命令を表す。
(1)ロード、ストア、ロードアドレス命令
命 令 | 書き方 | 命令の説明 | FRの設定 | |
命令コード | オペランド | |||
ロード LoaD |
LD | r1,r2 | r1 ←(r2) | ○*1 |
r,adr[,x] | r←(実効アドレス) | |||
ストア STore |
ST | r,adr[,x] | (実効アドレス)←(r) | - |
ロードアドレス Load ADdress |
LAD | r,adr[,x] | r ←実効アドレス | - |
- LD
void LD( int16_t& r1, int16_t r2 ){ r1 = r2; FR.OF = false; FR.SF = ( r1 < 0 ); FR.ZF = ( r1 == 0 ); } void LD( int16_t& r, int16_t adr, int16_t x ){ LD( r, *reinterpret_cast<const int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
- ST
void ST( int16_t r, int16_t adr, int16_t x ){ *reinterpret_cast<int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)) = r; }
- LAD
void LAD( int16_t& r, int16_t adr, int16_t x ){ r = static_cast<int16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); }
(2)算術、論理演算命令
命 令 | 書き方 | 命令の説明 | FRの設定 | |
命令コード | オペランド | |||
算術加算 ADD Arithmetic |
ADDA | r1,r2 | r1 ←(r1)+(r2) | ○ |
r,adr[,x] | r ←(r)+(実効アドレス) | |||
論理加算 ADD Logical |
ADDL | r1,r2 | r1 ←(r1)+L(r2) | ○ |
r,adr[,x] | r ←(r)+L(実効アドレス) | |||
算術減算 SUBtract Arithmetic |
SUBA | r1,r2 | r1 ←(r1)-(r2) | ○ |
r,adr[,x] | r ←(r)-(実効アドレス) | |||
論理減算 SUBtract Logical |
SUBL | r1,r2 | r1 ←(r1)-L(r2) | ○ |
r,adr[,x] | r ←(r)-L(実効アドレス) | |||
論理積 AND |
AND | r1,r2 | r1 ←(r1)AND(r2) | ○*1 |
r,adr[,x] | r ←(r)AND(実効アドレス) | |||
論理和 OR |
OR | r1,r2 | r1 ←(r1)OR(r2) | ○*1 |
r,adr[,x] | r ←(r)OR(実効アドレス) | |||
排他的論理和 eXclusive OR |
XOR | r1,r2 | r1 ←(r1)XOR(r2) | ○*1 |
r,adr[,x] | r ←(r)XOR(実効アドレス) |
- ADDA
void ADDA( int16_t& r1, int16_t r2 ){ int32_t temp = static_cast<int32_t>(r1) + static_cast<int32_t>(r2); r1 = static_cast<int16_t>(static_cast<uint16_t>(static_cast<uint32_t>(temp)&0x0000ffffu)); FR.OF = (( r1 < 0 ) != ( temp < 0 )); FR.SF = ( r1 < 0 ); FR.ZF = ( r1 == 0 ); } void ADDA( int16_t& r, int16_t adr, int16_t x ){ ADDA( r, *reinterpret_cast<const int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
- ADD
void ADD( int16_t& r1, int16_t r2 ){ uint32_t temp = static_cast<uint32_t>(static_cast<uint16_t>(r1)) + static_cast<uint32_t>(static_cast<uint16_t>(r2)); r1 = static_cast<int16_t>(static_cast<uint16_t>(temp&0x0000ffffu)); FR.OF = !!( temp & 0xffff0000u ); FR.SF = ( r1 < 0 ); FR.ZF = ( r1 == 0 ); } void ADD( int16_t& r, int16_t adr, int16_t x ){ ADD( r, *reinterpret_cast<const int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
- SUBA
void SUBA( int16_t& r1, int16_t r2 ){ int32_t temp = static_cast<int32_t>(r1) - static_cast<int32_t>(r2); r1 = static_cast<int16_t>(static_cast<uint16_t>(static_cast<uint32_t>(temp)&0x0000ffffu)); FR.OF = (( r1 < 0 ) != ( temp < 0 )); FR.SF = ( r1 < 0 ); FR.ZF = ( r1 == 0 ); } void SUBA( int16_t& r, int16_t adr, int16_t x ){ SUBA( r, *reinterpret_cast<const int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
- SUB
void SUB( int16_t& r1, int16_t r2 ){ uint32_t temp = static_cast<uint32_t>(static_cast<uint16_t>(r1)) - static_cast<uint32_t>(static_cast<uint16_t>(r2)); r1 = static_cast<int16_t>(static_cast<uint16_t>(temp&0x0000ffffu)); FR.OF = !!( temp & 0xffff0000u ); FR.SF = ( r1 < 0 ); FR.ZF = ( r1 == 0 ); } void SUB( int16_t& r, int16_t adr, int16_t x ){ SUB( r, *reinterpret_cast<const int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
- AND
void AND( int16_t& r1, int16_t r2 ){ r1 = static_cast<int16_t>(static_cast<uint16_t>(r1) & static_cast<uint16_t>(r2)); FR.OF = 0; FR.SF = ( r1 < 0 ); FR.ZF = ( r1 == 0 ); } void AND( int16_t& r, int16_t adr, int16_t x ){ AND( r, *reinterpret_cast<const int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
- OR
void OR( int16_t& r1, int16_t r2 ){ r1 = static_cast<int16_t>(static_cast<uint16_t>(r1) | static_cast<uint16_t>(r2)); FR.OF = 0; FR.SF = ( r1 < 0 ); FR.ZF = ( r1 == 0 ); } void OR( int16_t& r, int16_t adr, int16_t x ){ OR( r, *reinterpret_cast<const int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
- XOR
void XOR( int16_t& r1, int16_t r2 ){ r1 = static_cast<int16_t>(static_cast<uint16_t>(r1) ^ static_cast<uint16_t>(r2)); FR.OF = 0; FR.SF = ( r1 < 0 ); FR.ZF = ( r1 == 0 ); } void XOR( int16_t& r, int16_t adr, int16_t x ){ XOR( r, *reinterpret_cast<const int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
(3)比較演算命令
命 令 | 書き方 | 命令の説明 | FRの設定 | |
命令コード | オペランド | |||
算術比較 ComPare Arithmetic |
CPA | r1,r2 | (r1)と(r2)、又は(r)と(実効アドレス)の算術比較を行い、比較結果によって、FRに次の値を設定する。 | ○*1 |
r,adr[,x] | ||||
論理比較 ComPare Logical |
CPL | r1,r2 | (r1)と(r2)、又は(r)と(実効アドレス)の論理比較を行い、比較結果によって、FRに次の値を設定する。 | ○*1 |
r,adr[,x] |
比較結果 | FRの値 | |
SF | ZF | |
(r1)>(r2) | 0 | 0 |
(r)>(実効アドレス) | ||
(r1)=(r2) | 0 | 1 |
(r)=(実効アドレス) | ||
(r1)<(r2) | 1 | 0 |
(r)<(実効アドレス) |
void CPA( int16_t r1, int16_t r2 ){ FR.OF = 0; FR.SF = ( r1 < r2 ); FR.ZF = ( r1 == r2 ); } void CPA( int16_t r, int16_t adr, int16_t x ){ CPA( r, *reinterpret_cast<const int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
void CPL( int16_t r1, int16_t r2 ){ FR.OF = 0; FR.SF = (static_cast<uint16_t>(r1) < static_cast<uint16_t>(r2)); FR.ZF = (static_cast<uint16_t>(r1) == static_cast<uint16_t>(r2)); } void CPL( int16_t r, int16_t adr, int16_t x ){ CPL( r, *reinterpret_cast<const int16_t*>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
(4)シフト演算命令
命 令 | 書き方 | 命令の説明 | FRの設定 | |
命令コード | オペランド | |||
算術左シフト Shift Left Arithmetic |
SLA | r,adr[,x] | (r)を実効アドレスで指定したビット数だけ符号を除き左にシフトする。 シフトの結果、空いたビット位置には、0 が入る。 |
○*2 |
算術右シフト Shift Right Arithmetic |
SRA | r,adr[,x] | (r)を実効アドレスで指定したビット数だけ符号を除き右にシフトする。 シフトの結果、空いたビット位置には、符号と同じものが入る。 |
○*2 |
論理左シフト Shift Left Logical |
SLL | r,adr[,x] | (r)を実効アドレスで指定したビット数だけ符号を含み左にシフトする。 シフトの結果、空いたビット位置には 0 が入る。 |
○*2 |
論理右シフト Shift Right Logical |
SRL | r,adr[,x] | (r)を実効アドレスで指定したビット数だけ符号を含み右にシフトする。 シフトの結果、空いたビット位置には 0 が入る。 |
○*2 |
- 参考資料 3.シフト演算命令におけるビットの動き
- 算術左シフトでは、
OF には、ビット番号 14 の値が設定される。
ビット番号 0 の値には、0 が設定される。
ビット番号 15 の値は変化しない。 - 算術右シフトでは、
OF には、ビット番号 0 の値が設定される。
ビット番号 15 の値は変化しない。 - 論理左シフトでは、
OF には、ビット番号 15 の値が設定される。
ビット番号 0 の値には、0 が設定される。 - 論理右シフトでは、
OF には、ビット番号 0 の値が設定される。
ビット番号 15 の値には、0 が設定される。
- 算術左シフトでは、
void SLA( int16_t& r, int16_t adr, int16_t x ){ uint16_t shift_count = static_cast<uint16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); int16_t temp = static_cast<int16_t>( static_cast<uint16_t>(r) << shift_count ); r = static_cast<int16_t>(( static_cast<uint16_t>(r) & 0x8000u ) | ( static_cast<uint16_t>(temp) & 0x7fffu )); FR.OF = ( temp < 0 ); FR.SF = ( r < 0 ); FR.ZF = ( r == 0 ); }
- SRA
void SRA( int16_t& r, int16_t adr, int16_t x ){ uint16_t shift_count = static_cast<uint16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); uint32_t temp = ( static_cast<uint32_t>(static_cast<uint16_t>(r)) << 16 ) >> shift_count; r = static_cast<int16_t>(static_cast<uint16_t>( temp >> 16 )); FR.OF = !!( temp & 0x8000u ); FR.SF = ( r < 0 ); FR.ZF = ( r == 0 ); }
- SLL
void SLL( int16_t& r, int16_t adr, int16_t x ){ uint16_t shift_count = static_cast<uint16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); uint32_t temp = static_cast<uint32_t>(static_cast<uint16_t>(r) << shift_count ); r = static_cast<int16_t>(static_cast<uint16_t>( temp & 0x0000ffffu )); FR.OF = !!( temp & 0x00010000u ); FR.SF = ( r < 0 ); FR.ZF = ( r == 0 ); }
- SRL
void SRL( int16_t& r, int16_t adr, int16_t x ){ uint16_t shift_count = static_cast<uint16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); uint32_t temp = ( static_cast<uint32_t>(static_cast<uint16_t>(r)) << 16 ) >> shift_count; r = static_cast<int16_t>(static_cast<uint16_t>( temp >> 16 )); FR.OF = !!( temp & 0x00008000u ); FR.SF = ( r < 0 ); FR.ZF = ( r == 0 ); }
(5)分岐命令
命 令 | 書き方 | 命令の説明 | FRの設定 | |
命令コード | オペランド | |||
正分岐 Jump on PLus |
JPL | adr[,x] | FRのSFとZFが共に 0 なら指定の実効アドレスに分岐する。 分岐しないときは、次の命令に進む。 |
- |
負分岐 Jump on MInus |
JMI | adr[,x] | FRのSFが 1 なら指定の実効アドレスに分岐する。 分岐しないときは、次の命令に進む。 |
- |
非零分岐 Jump on Non Zero |
JNZ | adr[,x] | FRのZFが 0 なら指定の実効アドレスに分岐する。 分岐しないときは、次の命令に進む。 |
- |
零分岐 Jump on ZEro |
JZE | adr[,x] | FRのZFが 1 なら指定の実効アドレスに分岐する。 分岐しないときは、次の命令に進む。 |
- |
オーバフロー分岐 Jump on OVerflow |
JOV | adr[,x] | FRのOFが 1 なら指定の実効アドレスに分岐する。 分岐しないときは、次の命令に進む。 |
- |
無条件分岐 unconditional JUMP |
JUMP | adr[,x] | 無条件に指定の実効アドレスに分岐する。 | - |
命令 | 分岐するときの FRの値 | ||
OF | SF | ZF | |
JPL | 0 | 0 | |
JMI | 1 | ||
JNZ | 0 | ||
JZE | 1 | ||
JOV | 1 |
void JPL( int16_t adr, int16_t x ){ if( !FR.SF && !FR.ZF ){ PR = static_cast<int16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); } }
- JMI
void JMI( int16_t adr, int16_t x ){ if( FR.SF ){ PR = static_cast<int16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); } }
- JNZ
void JNZ( int16_t adr, int16_t x ){ if( !FR.ZF ){ PR = static_cast<int16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); } }
- JZE
void JZE( int16_t adr, int16_t x ){ if( FR.ZF ){ PR = static_cast<int16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); } }
- JOV
void JOV( int16_t adr, int16_t x ){ if( FR.OF ){ PR = static_cast<int16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); } }
- JUMP
void JUMP( int16_t adr, int16_t x ){ PR = static_cast<int16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); }
(6)スタック操作命令
命 令 | 書き方 | 命令の説明 | FRの設定 | |
命令コード | オペランド | |||
プッシュ PUSH |
PUSH | adr[,x] | SP ←(SP)-L 1、 (SP)←実効アドレス |
- |
ぽップ POP |
POP | r | r ←((SP))、 SP ←(SP)+L 1 |
- |
- PUSH
void PUSH( int16_t adr, int16_t x ){ --SP; *reinterpret_cast<int16_t*>(SP) = static_cast<int16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); }
- POP
void POP( int16_t& r ){ r = *reinterpret_cast<const int16_t*>(SP); ++SP; }
(7)コール、リターン命令
命 令 | 書き方 | 命令の説明 | FRの設定 | |
命令コード | オペランド | |||
コール CALL subroutine |
CALL | adr[,x] | SP ←(SP)-L 1、 (SP)←(PR)、 PR ←実効アドレス |
- |
リターン RETurn from subroutine |
RET | PR ←((SP))、 SP ←(SP)+L 1 |
- |
- CALL
void CALL( int16_t adr, int16_t x ){ --SP; *reinterpret_cast<int16_t*>(SP) = PR; PR = static_cast<int16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x)); }
- RET
void RET(){ PR = *reinterpret_cast<const int16_t*>(SP); ++SP; }
(8)その他
命 令 | 書き方 | 命令の説明 | FRの設定 | |
命令コード | オペランド | |||
スーパバイザコール SuperVisor Call |
SVC | adr[,x] | 実効アドレスを引数として割出しを行う。 実行後の GRと FRは不定となる。 |
- |
ノーオペレーション No OPeration |
NOP | 何もしない。 | - |
void SVC( int16_t adr, int16_t x ){ SVC_implementation( static_cast<int16_t>(static_cast<uint16_t>(adr) + static_cast<uint16_t>(x))); }
- NOP
void NOP(){
}
(注)
- r、r1、r2
いずれも GRを示す。
指定できる GRは GR0〜 GR7 - adr
アドレスを示す。
指定できる値の範囲は 0〜 65535 - x
指標レジスタとして用いる GRを示す。
指定できる GRは GR1〜 GR7 - [ ]
[ ]内の指定は省略できることを示す。 - ( )
( )内のレジスタ又はアドレスに格納されている内容を示す。 - 実効アドレス
adrと xの内容との論理加算値又はその値が示す番地 - ←
演算結果を、左辺のレジスタ又はアドレスに格納することを示す。 - +L、-L
論理加算、論理減算を示す。 - FRの設定
- ○
設定されることを示す。 - ○*1
設定されることを示す。
ただし、OFには 0が設定される。 - ○*2
設定されることを示す。
ただし、OFにはレジスタから最後に送り出されたビットの値が設定される。 - -
実行前の値が保持されることを示す。
- ○
参考資料 4. プログラムの例
COUNT1 START ; ; 入力 GR1:検索する語 ; 処理 GR1中の'1'のビットの個数を求める ; 出力 GR0:GR1 中の'1'のビットの個数 PUSH 0,GR1 ; PUSH 0,GR2 ; SUBA GR2,GR2 ; Count = 0 AND GR1,GR1 ; 全部のビットが'0'? JZE RETURN ; 全部のビットが'0'なら終了 MORE LAD GR2,1,GR2 ; Count = Count + 1 LAD GR0,-1,GR1 ; 最下位の'1'のビット1 個を AND GR1,GR0 ; '0'に変える JNZ MORE ; '1'のビットが残っていれば繰返し RETURN LD GR0,GR2 ; GR0 = Count POP GR2 ; POP GR1 ; RET ; 呼出しプログラムへ戻る END ;
↓
void COUNT1(){ // 入力 GR1:検索する語 // 処理 GR1中の'1'のビットの個数を求める // 出力 GR0:GR1 中の'1'のビットの個数 PUSH( 0, GR1 ); // PUSH( 0, GR2 ); // SUBA( GR2, GR2 ); // Count = 0 AND( GR1, GR1 ); // 全部のビットが'0'? JZE( RETURN ); // 全部のビットが'0'なら終了 MORE:; LAD( GR2, 1, GR2 ); // Count = Count + 1 LAD( GR0, -1, GR1 ); // 最下位の'1'のビット1 個を AND( GR1, GR0 ); // '0'に変える JNZ( MORE ); // '1'のビットが残っていれば繰返し RETURN:; LD( GR0, GR2 ); // GR0 = Count POP( GR2 ); // POP( GR1 ); // RET(); // 呼出しプログラムへ戻る }
↓
void COUNT1(){ auto prev_GR1 = GR1; auto prev_GR2 = GR2; GR2 -= GR2; // Count = 0 GR1 &= GR1; if( GR1 ){ // 全部のビットが'0'? do{ ++GR2; // Count = Count + 1 GR0 = GR1 - 1; // 最下位の '1' のビット1 個を GR1 &= GR0; // '0' に変える }while( GR1 ); // '1'のビットが残っていれば繰返し } GR0 = GR2; // GR0 = Count GR2 = prev_GR2; GR1 = prev_GR1; }
↓
int16_t COUNT1( int16_t r1 ){ int16_t r2 = 0; // Count = 0 if( r1 ){ // 全部のビットが'0'? do{ ++r2; // Count = Count + 1 int16_t r0 = r1 - 1; // 最下位の '1' のビット1 個を r1 &= r0; // '0' に変える }while( r1 ); // '1'のビットが残っていれば繰返し } return r2; // GR0 = Count }
↓
int16_t COUNT1( int16_t r1 ){ int16_t r2 = 0; // Count = 0 while( r1 ){ // 全部のビットが'0'? ++r2; // Count = Count + 1 r1 &= r1 - 1; // 最下位の '1' のビット1 個を '0' に変える // '1'のビットが残っていれば繰返し } return r2; // GR0 = Count }