Featured image of post V53 VMEシステムで遊んでみました #9 割り込みで時を刻む

V53 VMEシステムで遊んでみました #9 割り込みで時を刻む

前回はABORTスイッチを押すと発生するNMIでプログラムを中断し、デバック情報が表示されるようにしました。今回はuPD71059(PIC)の割り込みコントローラの仕様を調査します。

気になるジャンパーピン

割り込みコントローラーuPD71059(PIC)とV53 CPUの近くに10x4のピンヘッダW19があり、上2段と下2段にわかれてジャンパが取り付けられています。

気になるジャンパーピン

これが割り込みに関係するのではないかと推測し、このジャンパーを中心にテスターで回路を調べてみました。まずはPICへの割り込み入力信号はジャンパーの最上段に並んでいました。
上から2列目のラインはペリフェラルデバイスの割り込み出力に接続されていました。
次の3列目のラインはV53 ICUの割り込み入力に接続されていました。
最後の4列目のラインは接続先が見つかりませんでした。直結ではなく間にゲートやGALなどがあるのかもしれません。

これらを回路図にまとめると次のようになりました。

テスターで分かった範囲の回路図

着目すべき点はPICの割り込み信号がV53 ICUのINTP7に接続されV53 ICUとPICはカスケード接続になっていることがわかります。つまりPICの割り込みを使う場合はICUでも割り込みの設定が必要になります。

PIC割り込みを使ってみる

PICのINTP0に接続されているTimer 0を使ってTickを設定してみます。ここでは10msごとに割り込みを発生させて動作確認を行います。

PICとV53 ICUの初期化

PICの初期化とV53 ICUの初期化を行います。PICのI/Oアドレスは0x00c8、0x00caです。ICUのI/Oアドレスは0x1280を割り当てました。なお、OPSELでICUを有効化する必要もあります。

%define PIC_REG0    0x00c8  ; μPD71059 (PIC)	IRR/ISR/IW1/PCFW/MCW	
%define PIC_REG1    0x00cA  ; μPD71059 (PIC)	IMR/IW2/IW3/IW4

    ;------------------------------------------
    ; μPD71059 (PIC) 初期化
    ;------------------------------------------
    mov al, 13h         ; ICW1: Edge, Single, ICW4 needed
    out PIC_REG0, al

    mov al, 20h         ; ICW2: Vector Offset = 20h (INT 32)
    out PIC_REG1, al
                        ; ICW3 is skipped in Single Mode
    mov al, 01h         ; ICW4: 8086 Mode, Normal EOI
    out PIC_REG1, al

    mov al, 0FEh        ; OCW1: Unmask IR0 (Timer) only. (1111 1110)
    out PIC_REG1, al

    ;------------------------------------------
    ; ICUの追加設定
    ;------------------------------------------
    ; 1. 内蔵周辺機能の有効化 (OPSEL)
    ;------------------------------------------
    mov  dx, OPSEL
    in   al, dx
    or   al, 00000010b   ; Bit 1 (ICU) をセット
    out  dx, al

    ;------------------------------------------
    ; 2. レジスタ配置アドレスの設定 (IULA)
    ;------------------------------------------
    mov dx, IULA
    mov al, 0x80    ; 下位アドレス
    out dx, al

    ;------------------------------------------
    ; 3. 割り込みマスクの設定 (Enable INTP7)
    ;------------------------------------------
    mov dx, ICU_REG0
    mov al, 13h     ; ICW1: Edge, Single, ICW4 needed
    out dx, al

    mov dx, ICU_REG1
    mov al, 20h     ; ICW2: Vector Offset = 20h (INT 32)
    out dx, al
                    ; ICW3 is skipped in Single Mode
    mov al, 01h     ; ICW4: 8086 Mode, Normal EOI
    out dx, al

    ; Bit 7 (INTP7) を 0 (許可) にします。
    in  al, dx
    and al, 7Fh     ; 0111 1111 (Bit 7をクリア)
    out dx, al

    ;-------------------------------------------
    ; 割り込み許可
    ;-------------------------------------------
    sti

タイマーの設定

割り込みタイミングであるタイマー0の設定はRAMモニタの初期化で行っていますので、10ms間隔で割り込みがかかるようにTimer 0の分周比を変更します。
ベースクロックは1.2288MHzですので、これを100Hzにするには1.2288MHz/11288=100Hzとなり、11288を16進数にすると3000hとなります。これをV53 SCU Timer 0に設定します。

    ;------------------------------------------
    ; 4. カウンタ値 (分周比) のロード
    ;------------------------------------------
    ; Timer 0 (INTP0) 100Hz
    mov  dx, TM0_CNT
    mov  al, 0
    out  dx, al
    mov  al, 0x30
    out  dx, al

これで100Hzの信号がTimer 0から出力されます。テスターで100Hzの信号がPICのINTP0に来ていることを確認しました。

割り込みハンドラの実装

割り込みハンドラは割り込みが行われたときに実行されるプログラムになります。今回はシンプルにUSARTポートにドットを出力するようにしました。モニタコンソールとは別なのでこのようなデバックに便利です。ソースコードは以下になりました。これをRAMモニタのソースに加えます。

;==========================================================
; Tick Handler
;==========================================================
_tick_handler:
    push ax
    push dx

    ; シリアルポートに'.'を出力
    mov al, '.'          ; debug char
    out USART_DATA, al   ; USARTのDataポートに出力

    ; --- EOI (End of Interrupt) to PIC ---
    mov al, 20h     ; Non-specific EOI

    ; 外部PIC (Slave) の割り込み完了
    out PIC_REG0, al

    ; V53内蔵ICU (Master) の割り込み完了
    mov dx, ICU_REG0 
    out dx, al

    pop dx
    pop ax
    iret

割り込みベクタの実装

割り込みベクタテーブルの設定を行います。最終的にICUに割り込みがかかるのでICUのINTP7に対応する割り込みベクタにTickハンドラのアドレスを設定します。このあたりの設定方法はNMIと同じです。INTP7はベクタ27になるので、テーブルは009chからの4バイトになります。

; Tick用割り込みベクタテーブル (Vector 27h = V53 INTP7)
; 0x27 * 4 = 9Ch
mov  di, 0x009c      ; Vector 27h Address offset
mov  ax, _tick_handler ; ハンドラのアドレス (IP)
stosw                ; [0000:009c] = AX
mov  ax, cs          ; 現在のCS
stosw                ; [0000:009e] = CS

これらの変更をRAMモニタのソースに加えてアセンブルし、RAMモニタを実行したところ、USARTシリアルにドットを出力しながら、モニタコマンドが問題なく使えることが確認しました。

Timerコマンドを実装する

割り込みの確認テスト用としてドットの文字をシリアルに出力するだけでしたが、今後実用的に使えるように32bitのTickカウンタをカウントアップする機能を割り込みハンドラに実装しました。

;==========================================================
; Tick Handler
;==========================================================
_tick_handler:
    push ax
    push dx

    ; CS (コードセグメント) と DS (データセグメント) を合わせる
    ; (Tiny Model的な動作のため)
    mov ax, cs
    mov ds, ax

    ; --- 32bit カウンタのインクリメント ---
    inc word [tick_counter_lo]
    jnz .skip_carry
    inc word [tick_counter_hi]
.skip_carry:

    ; --- EOI (End of Interrupt) to PIC ---
    mov al, 20h     ; Non-specific EOI

    ; 外部PIC (Slave) の割り込み完了
    out PIC_REG0, al

    ; V53内蔵ICU (Master) の割り込み完了
    mov dx, ICU_REG0 
    out dx, al

    pop dx
    pop ax
    iret

またTickの値がわかるようにモニタにTimerコマンドを追加し、指定した時間だけ待つ機能とTickカウンタの表示機能を実装しました。

; ==============================================================
; Command: Timer (ms)
;   T          -> Show current tick counter (32-bit HEX)
;   T <val>    -> Wait for <val> ticks (10ms unit)
; ==============================================================
do_timer:
    call getc_echo      ; コマンド('T')の次の文字を取得
    cmp al, 0x0D        ; Enterキー(CR)なら表示モードへ
    je .show_counter
    cmp al, ' '         ; スペースならWaitモードへ
    jne error           ; それ以外はエラー

    ; --- Wait Mode (引数あり: T 0064 等) ---
    call get_hex_word   ; AX = 待ち時間 (0064h = 1秒)

    ; 開始時刻を保存 (Start Time)
    mov bx, [tick_counter_lo]

.wait_loop:
    ; 1. 現在時刻を取得
    mov cx, [tick_counter_lo]
    
    ; 2. 経過時間を計算 (Current - Start)
    ; オーバーフローしても、この引き算の結果は正しい経過時間になる
    sub cx, bx          
    
    ; 3. 経過時間 < 待ち時間 ならループ継続
    cmp cx, ax
    jb .wait_loop       ; Jump if Below (unsigned compare)

    ; 指定時間経過した
    mov si, msg_done
    call puts
    jmp monitor_loop

.show_counter:
    ; --- Show Counter Mode (引数なし: Tのみ) ---
    call putc_crlf
    
    mov si, msg_tick    ; "Tick: "
    call puts

    ; 32bitカウンタを [High][Low] の順で表示
    mov ax, [tick_counter_hi]
    call print_hex_word
    mov ax, [tick_counter_lo]
    call print_hex_word
    
    call putc_crlf
    jmp monitor_loop

; 変数領域
tick_counter_lo: dw 0x0000  ; Tick: 下位16bit
tick_counter_hi: dw 0x0000  ; Tick: 上位16bit

Timerコマンドの実行結果は以下のようになります。Tick変数が増加しているのが確認できます。また指定した時間を経過するとDoneが表示されました。

**  V53 RAM MONITOR v0.7 2026-01-31  **
> t
Tick: 000000EB
> t
Tick: 000001D7
> t
Tick: 000002FD
> t 0064 Done  ←1秒後(0064h: 100x10ms)にDoneが表示されます。
> t 1770 Done  ←1分後(1770h: 6000x10ms)にDoneが表示されます。
>

まとめ

今回はPICとICUを使ってタイマー割り込みでTickカウンタのシンプルな実装を行いました。
次回はまだ用途がわかっていないV53 ICU割り込みを探ります。たぶんVMEバスのIRQと関連していると思われますが、それを確認できるように割り込みハンドラを実装していきます。
また、現在のPICの割り込みハンドラはTimer 0の機能しか処理できません。これを他の割り込み入力にも対応する必要があります。まだまだ割り込みは奥が深いので楽しみです。

次の記事:V53 VMEシステムで遊んでみました #10 割り込みを攻略する

Hugo で構築されています。
テーマ StackJimmy によって設計されています。