Z80 でフレームレートを固定にする処理の解説です。よくあるのは垂直ブランキング待ちの手法です。ただ、これだと NTSC では 1/60 固定になりますし、また、処理落ちした際は、いきなり次のブランキングまで待たされてしまいます。そこで 2ms 割り込みがある機種では次の考え方を採用しました。

まず、2ms 割り込みでは、割り込みが起きる度にカウンターを +1 します。ここではワークエリアの名前を INT.Counter とします。

ld hl, INT.Counter inc (hl)
次に、メインループの始まりでそのカウント値を保存します。ROM の場合は RAM のワークに保存となりますが、今回はプログラムが RAM にあるとして、自己書き換えを行います。.witchk が自己書き換え先を示すラベルです。※後述

.loop ld a, (INT.Counter) ld (.witchk), a
メインループの最後に経過時間を求めて、それが予定の時間より短ければ時間待ちします。

.waitlp ld a, (INT.Counter) .witchk equ $ + 1 sub 0 cp DFN.GAME_SPEED jp nc, .loop jr .waitlp
現在のカウント値から、以前のカウント値を引くと、経過時間が得られます。もし、桁あふれが起きていても問題ありません。例えば、以前のカウント値が $FE だとして、現在が $03 だとすると、$03 - $FE = $05 となり、正しい経過時間を得られます。DFN.GAME_SPEED はウエイト値です。例えば 50FPS でループさせたい場合は、1000ms÷50FPS÷2ms = 10 となりますので、DFN.GAME_SPEED は 10 を設定します。
※たった割り込み10回分しかないんですね…

さて、ここで処理落ちを検知することも出来ます。先の .waitlp の直前に、経過時間を算出して、この時点で予定の時間を超えていたら、処理落ちとみなすことが出来ます。

ld hl, .witchk ld a, (INT.Counter) sub (hl) cp DFN.GAME_SPEED jr nc, .over