現在地

COSMACでサブルーチン


カテゴリー:

前回書いたようにCOSMACにはサブルーチン呼び出しの仕組みがありません。

それではどうするかというとSEP命令を使います。

COSMACはメモリをポイントできるレジスタを16個 R(0)R(F)と、4ビットのレジスタPを持っています。R(0)R(F)の内Pで選ばれたR(P)がプログラムカウンタとして使用されます。

SEP命令でPに新たな値を設定することでサブルーチンを呼び出し、Pに元の値を設定することで戻ることができます。

       4/       0 :                     MAIN:
       5/       0 :                     	;; Main routine (part 1)
       6/       0 : F8 00               	LDI	(SUB >> 8)
       7/       2 : B4                  	PHI	4
       8/       3 : F8 07               	LDI	(SUB & 0FFH)
       9/       5 : A4                  	PLO	4
      10/       6 : D4                  	SEP	4		; CALL
      11/       7 :                     	;; Main routine (part 2)
      12/       7 :                     
      13/       7 :                     SUB:
      14/       7 :                     	;; Sub routine
      15/       7 : D3                  	SEP	3		; RETURN

メインルーチンはP=3の状態で実行されていたとします。

6~9行目で呼びたいサブルーチンのアドレスをR(4)に入れます。

10行目を実行するとそのR(4)が新たなプログラムカウンタになるので13行目からのサブルーチンに実行が移ります。R(3)にはSEP命令の次のアドレスが入ったままとなり、これはもうプログラムカウンタではないのでそのまま更新されなくなります。

サブルーチンの実行が終わって15行目でSEP命令を実行すると再びR(3)がプログラムカウンタになるのでメインルーチンの続きを実行することになります。

この方式は簡単ですが、欠点もあります。

  1. 呼び出しの段数が増えるとR()レジスタを消費してしまう
  2. サブルーチンは呼び出し元のPを知る必要がある
  3. 再帰呼び出しができない

Universal Monitor程度だと1.,3.は問題ありません(F8へUniversal Monitorを移植(その1)参照)が、2.は問題です。

例えばコンソール出力(CONOUT)はメインルーチンやSTROUTなどいくつかの段から呼ばれる可能性があります。サブルーチンで元のPの値を知るすべはありませんから、何段目かを固定し場合によってはダミーのルーチンを挟むなどの対策が必要になります。

最初はSC/MPのようにマクロを書いて対処しようかとも思ったのですが...

結構大掛かりになりそうだったのと、RCA標準の方法に合わせたほうが良さそう、とのことから "Standard Call and Return Technique" と呼ばれているものを使うことにしました。

これはプログラムのどこかに以下の3つのルーチンを置きます。

      19/       0 :                     	;; SCRT initialization
      20/       0 : F8 00               	LDI	high(CSTART)
      21/       2 : B3                  	PHI	PC
      22/       3 : F8 37               	LDI	low(CSTART)
      23/       5 : A3                  	PLO	PC
      24/       6 :                     
      25/       6 : F8 00               	LDI	high(CALLR)
      26/       8 : B4                  	PHI	CALL
      27/       9 : F8 1B               	LDI	low(CALLR)
      28/       B : A4                  	PLO	CALL
      29/       C : F8 00               	LDI	high(RETR)
      30/       E : B5                  	PHI	RETN
      31/       F : F8 2B               	LDI	low(RETR)
      32/      11 : A5                  	PLO	RETN
      33/      12 : F8 0F               	LDI	high(STACK)
      34/      14 : B2                  	PHI	SP
      35/      15 : F8 EF               	LDI	low(STACK)
      36/      17 : A2                  	PLO	SP
      37/      18 : E2                  	SEX	SP
      38/      19 : D3                  	SEP	PC		; Jump to CSTART
      39/      1A :                     
      40/      1A :                     	;; SCRT: Standard CALL
      41/      1A :                     EXITC:
      42/      1A : D3                  	SEP	PC
      43/      1B :                     CALLR:
      44/      1B : E2                  	SEX	SP
      45/      1C : 96                  	GHI	LINK		; Save current LINK to STACK
      46/      1D : 73                  	STXD
      47/      1E : 86                  	GLO	LINK
      48/      1F : 73                  	STXD
      49/      20 : 93                  	GHI	PC		; Copy PC to LINK
      50/      21 : B6                  	PHI	LINK
      51/      22 : 83                  	GLO	PC
      52/      23 : A6                  	PLO	LINK
      53/      24 : 46                  	LDA	LINK		; PC.h = (LINK)+
      54/      25 : B3                  	PHI	PC
      55/      26 : 46                  	LDA	LINK		; PC.l = (LINK)+
      56/      27 : A3                  	PLO	PC
      57/      28 : 30 1A               	BR	EXITC
      58/      2A :                     
      59/      2A :                     	;; SCRT: Standard RETuRn
      60/      2A :                     EXITR:
      61/      2A : D3                  	SEP	PC		; Return to Main Program
      62/      2B :                     RETR:
      63/      2B : 96                  	GHI	LINK		; Recover calling program return address
      64/      2C : B3                  	PHI	PC
      65/      2D : 86                  	GLO	LINK
      66/      2E : A3                  	PLO	PC
      67/      2F : E2                  	SEX	SP
      68/      30 : 12                  	INC	SP
      69/      31 : 72                  	LDXA			; Restore the contents of LINK
      70/      32 : A6                  	PLO	LINK
      71/      33 : F0                  	LDX
      72/      34 : B6                  	PHI	LINK
      73/      35 : 30 2A               	BR	EXITR

一つ目の19~38行は初期化ルーチンです。COSMACはリセット後はP=0で走り始めますが、ここでR(2)R(5)を初期化し、以後は原則としてP=3の状態で実行されます。以後、以下のレジスタは壊してはいけません。

  • R(2)=SP :スタックポインタ
  • R(3)=PC :プログラムカウンタ
  • R(4)=CALL :常にCALLRルーチンのアドレスを入れておく
  • R(5)=RETN :常にRETRルーチンのアドレスを入れておく
  • R(6)=SP :実行中のサブルーチンからの戻りアドレス

サブルーチンを呼び出したいときは以下のようにします。

      77/      37 : D4                  	SEP	CALL
      78/      38 : 07 00               	DW	INIT

これで二つ目の43行目からが(R(4)にアドレスが入っている)実行されます。

44~48行でR(6)に入っている戻りアドレスをスタックに退避します。

49~52行でR(3)に入っているSEP CALLの次のアドレスをR(6)にコピーします。

53~56行ではR(6)DW INITのアドレス)から呼び出し先のアドレス(この場合はINIT)を読み込んでR(3)に入れます。

57行目から42行に行ってR(3)に入っている呼び出し先へ飛びます。ここでなぜ57行目にSEP PCを書かないかというと、SEP実行後にR(4)をまた元の値に戻して次のSEP CALLの準備をさせるためです。

サブルーチンから戻りたいときは以下のようにします。

    1088/     5D9 : D5                  	SEP	RETN

上のSEP CALLがわかればこれも簡単でしょう。

スタックを使っているのでメモリの許す限り多段呼び出しできますし、再帰呼び出しも問題ありません。またこのCALLRRETRを実行している時以外は常にP=3なので何段目に呼ばれても大丈夫です。

参考文献・関連図書: 
"User Manual for the CDP1802 COSMAC Microprocessor", RCA.

コメントを追加

Plain text

  • HTMLタグは利用できません。
  • ウェブページアドレスとメールアドレスは、自動的にハイパーリンクに変換されます。
  • 行と段落は自動的に折り返されます。
※ コメントは原則公開です。個別のご相談などは「ご意見・ご要望」からお願いします。