歩いたら休め

If the implementation is easy to explain, it may be a good idea.

【GB】gbdkで割り込み処理を実装しました

ボタンを押すたびにカウントアップする実装です。

github.com

といっても大したことはなく、CPUにボタン押下時の割り込み処理が存在するので、そのときに実行する関数を指定しているだけです。

私は今までOSが存在しない低レイヤーのプログラミングをしたことがなく、「割り込み」の機能がOSレイヤーで実装されていると思いこんでいた(もしかしたらOSレイヤーでも実装されているのかもしれませんが)ので、CPUの機能として実装されていることがちょっと意外でした。

低レベルプログラミング

低レベルプログラミング

『低レベルプログラミング』から引用すると、フォン・ノイマンアーキテクチャから現代のCPU(書籍ではIntel 64が例ですが)いくつかの拡張があるようです。

このうち「割り込み」によって、対話的なプログラミングできるようになったそうです。このうちゲームボーイでは「プロテクションリング」「仮想メモリ」のような、プログラムの機構は無さそうですね。また、以前海外の記事で「GBのアーキテクチャがGBA以前は"register-oriented strategy"向けに設計されていて、関数でスタックを利用するC言語では遅い場合がある」というような話を読んだことあるのですが、それはハードウェアスタックが存在しないことを指しているのかもしれません。

ただ、割り込みの実装自体はできたものの、ライフゲームの実装をするためにはいくつか困った点があります。

kiito.hatenablog.com

割り込み処理中に joypad() でボタンの情報を取得できない

もともと、以下のようにグローバル変数joypad_state )を書き換え、ボタンの処理によって分岐しようとしていました。

int main(void) {
    disable_interrupts();
    add_JOY(onjoy);
    enable_interrupts();
    set_interrupts(JOY_IFLAG);

    while (1) {
        switch (joypad_state) {
            // 分岐
        }
    }
}

void onjoy(void) {
    joypad_state = joypad();
}

ただ、公式ドキュメントにもある通り、 add_JOY の処理中には、ボタンの状態を受け取れません( joypad 関数が動作しません)でした。これは少し工夫する必要があります。

gbdk.sourceforge.net

Interrupts in GBDK are handled using the functions disable_interrupts(), enable_interrupts(), set_interrupts(UBYTE ier) and the interrupt service routine (ISR) linkers add_VBL, add_TIM, add_LCD, add_SIO and add_JOY which add interrupt handlers for the vertical blank, timer, LCD, serial and joypad interrupts respectively.

set_interrupts をした場合に画面の表示がズレてしまう

#include <gb/gb.h>
#include <gb/drawing.h>

void onjoy(void);

int main(void) {
    disable_interrupts();
    add_JOY(onjoy);
    enable_interrupts();
    // set_interrupts(JOY_IFLAG); // <- ここをコメントアウトしたりする
    line(16, 4, 16, 140);
}

コメントアウトした場合はこうなのですが、

コメントアウトを外し、 set_interrupts を行うとこうなります。

こういう場合のデバッグ方法が分からずに困っています。コンパイル後のアセンブリを見ていくしかないと思うんですが、どういう観点で見れば良いのか分かりません。