You are here

80系アセンブラのテクニック


カテゴリー:

2日目の今日は80系(8080, Z80, 8085など)のちょっと懐かしいテクニックです。

今こんな書き方をすると、わかりにくいと怒られるか、パイプラインなどに悪影響が出たり技術的にもデメリットになったりしますので避けるべきですが、当時はそれなりにメリットもあってよく使われていました。

一つは命令の途中に飛び込むというものです。例えばこんな感じです。


   1:	8000          	    ORG 8000H
   2:	8000          	entry1:
   3:	8000  3EAF    	    LD  A,0AFH
   4:	8002  3200FF  	    LD  (0FF00H),A
   5:	8005  C9      	    RET

3行目でAレジスタに0AFHを入れ、4行目で0FF00H番地のメモリに書き込んで、リターンするだけのものです。
ここでは8000Hがエントリポイントですが、8001Hもエントリポイントと考えると以下のようになります。オブジェクトは上と一緒です。


   1:	8000          	    ORG 8000H
   2:	8000          	entry1:
   3:	8000  3E      	    DEFB  3EH
   4:	8001          	entry2:
   5:	8001  AF      	    XOR A
   6:	8002  3200FF  	    LD  (0FF00H),A
   7:	8005  C9      	    RET

5行目でAレジスタ同士のXORを取り(結果としてAレジスタは00Hになります)、メモリに書き込むところ以降は上と同じになります。
8000HをCALLすれば0FF00H番地に0AFHが書き込まれ、8001HをCALLすれば00Hが書き込まれます。

これを素直に書けばこうなります。


   1:	8000          	    ORG 8000H
   2:	8000          	entry1:
   3:	8000  3EAF    	    LD  A,0AFH
   4:	8002  1801    	    JR  L1
   5:	8004          	entry2:
   6:	8004  AF      	    XOR A
   7:	8005          	L1:
   8:	8005  3200FF  	    LD  (0FF00H),A
   9:	8008  C9      	    RET

これだと9バイトになりますから、3バイトの節約になっていたわけです。

一方、デメリットはいくつもあります。

  1. アセンブラで書きにくい。
  2. 値が自由に選べない。
  3. 3つ以上にできない。
  4. わかりにくい。

それでもコードサイズが小さくなること、実行速度的にも有利なので使われました。これはNECのPC-8001, PC-8001mk2などのN-BASICのROMでも使われていたはずです。
2,3は他のレジスタを壊しても良いなら回避する方法もありますが、数が増えると速度的には不利になるのであまり使われなかったと思います。
4は見方を変えるとメリット(プロテクト等での解析の妨害)だったかもしれませんね。

お次はサブルーチンコール時のリターンスタックをいじるものです。


   1:	9000          	    ORG     9000H
   2:	9000          	prtstr:
   3:	9000  E1      	    POP     HL
   4:	9001          	L1: 
   5:	9001  7E      	    LD      A,(HL)
   6:	9002  23      	    INC     HL
   7:	9003  B7      	    OR      A
   8:	9004  2805    	    JR      Z,L2
   9:	9006  CD5702  	    CALL    0257H
  10:	9009  18F6    	    JR      L1
  11:	900B          	L2:
  12:	900B  E5      	    PUSH    HL
  13:	900C  C9      	    RET

これは文字列(00Hで終端)を出力するルーチンですが、文字列のアドレスを明示的に渡す必要が無いというものです。
3行目でスタックトップをHLレジスタに取り出しますが、スタックトップにはこのサブルーチンからの戻りアドレスが入っています。5行目ではメモリから1文字のコードを取り出し、6行目でポインタを進めます。7行目はAレジスタ同士のOR演算、同じもののORをとっても値は変化しませんが、結果がフラグに反映されます(常套手段、ORの代わりにANDでもよい)。8行目は文字列の終端チェック、直前のORでAレジスタが00HならZフラグが立っているのでL2へ抜けます。
9行目では1文字出力ルーチン(0257HはN-BASICのROMルーチン)を呼び出して1文字表示します。10行目はL1に戻って次の文字の取り出しに進みます。
11行目のL2:ではHLレジスタは文字列終端の00Hの次のアドレスを指しています。12行目でこれをスタックトップに戻します。13行目のRET命令ではスタックトップに戻りアドレスがあるとしてそこに分岐しますが、それは文字列終端の次のアドレスに書き換わっています。

ここではわかりやすく書きましたが、最適化するなら12,13行目は「JP (HL)」に置き換えられます。

このテクニックはサブルーチンを効率化するためではなく、呼び出し側を簡単にするためにこそあります。呼び出し側はこんな感じになります。


   1:	8000          	    ORG     8000H
   2:	8000  CD0090  	    CALL    9000H
   3:	8003  53545249	    DEFB    'STRING',0
	      4E4700
   4:	800A          	L0:

2行目でいきなり呼び出しを実行します。文字列の場所を指定する必要はありません。CALL命令が自動的に戻りアドレスをスタックに積みますがそれが文字列の位置指定になっているからです。
3行目、CALL命令の直後に文字列を配置します。スタックには8003Hが積まれているのでこの文字列が出力されます。
戻ってくるときは8003Hではなく800AHに戻ってくるのでプログラムの続きは文字列の直後から配置しておけば良いのです。

コンパイル言語(TL/1だったと思います)のランタイムで使われているのを見つけたとき、「うまいなぁ~」と感心したのを憶えています。

上の例だとA,HLレジスタの内容が壊れますが、次のようにすると壊れないようにすることもできます。


   1:	9000          	    ORG     9000H
   2:	9000          	prtstr:
   3:	9000  E3      	    EX      (SP),HL
   4:	9001  F5      	    PUSH    AF
   5:	9002          	L1: 
   6:	9002  7E      	    LD      A,(HL)
   7:	9003  23      	    INC     HL
   8:	9004  B7      	    OR      A
   9:	9005  2805    	    JR      Z,L2
  10:	9007  CD5702  	    CALL    0257H
  11:	900A  18F6    	    JR      L1
  12:	900C          	L2:
  13:	900C  F1      	    POP     AF
  14:	900D  E3      	    EX      (SP),HL
  15:	900E  C9      	    RET

3行目でスタックトップを取り出す代わりに、HLレジスタと入れ替えます。また4行目でAレジスタとフラグもスタックに積んでおきます。これでレジスタを全く壊さない(CALL 0257Hがレジスタを壊さないな前提)ものができますが、EX (SP),HL命令は重い(時間がかかる)命令なので普通はここまでしません。
80系の場合、A,HLレジスタは使用頻度が高い(何をするにも使う必要がある)ので自動的に保存するより必要に応じて保存するほうが良いのです。「使用頻度が高いならなおさら保存するべきでは」と思うかもしれませんが、逆に一つの用途に長く使えないのでかえって保存しなくて良い場合が多いのです。

まだまだ「リロケートの技」とかZ80限定ですが「IX,IYの使い方」とか書きたいことはあります。もう何回か80系アセンブラの話は書くと思います。

ここに挙げたコード例は記憶を頼りに書いてクロスアセンブルしてリスティングファイルから抜粋しました。実行確認はしていないので間違いが含まれているかもしれません。お気付きの方は「ご意見・ご要望」にてご指摘いただけると助かります。
広告: 

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
※ コメントは原則公開です。個別のご相談などは「ご意見・ご要望」からお願いします。