state save (sound未対応)

概要

M72 のソフトでは V30 の NMI を使ってないのでゲームのステートセーブを実装できそうだと試してみました。
Z80, Z80向け RAM, サウンドデバイス(特に自分が作っていないYM2151)は対応が難しそうなので実装していません。

仕様

  • main CPU 領域のみの対応
  • save
    • 動作に関係のある RAM と CPU レジスタを全て SD カードのファイルへ書き出す
  • load
    • save で書き込んだファイルを読み込み、 RAM と CPU レジスタへ読み込ませる

実装

ホストCPUの 68000 側ではシステム制御待ちになっています。

  • システム制御要求が入ると 68000 は V30 へ NMI を発行。
  • V30 は NMI を受付け、作成した NMI ルーチンを実行。
  • NMI ルーチンの途中で V30 を停止 (HLDRQ を 有効にする)
  • 68000 は HLDAK を見張り、有効になり次第、コンソールを再開する
    • s コマンド (save) 実行時に M72 の main 関連 (V30 RAM と VRAM) の内容を SD カードのファイルへ保存する。
    • l コマンド (load) 実行時はファイルからRAMへ内容を復帰する。
  • V30 再開命令を受け付けると 68000 は HLDRQ を無効にする
  • 途中で止まっている NMI ルーチンを再開し、元のプログラムを実行する

NMI ルーチン

大きく分けて3つの流れがあります。

  • CPUレジスタとVRAMを RAM へダンプ
    • V30 の CPU レジスタ (AX,BX など)を stack へ push
    • 68000 から直接参照できない sprite attribute RAM と color RAM を普通の RAM へコピー
  • HLDRQ 制御 OUTPUT PORT へ write (この PORT は本物の M72 にはありません)
  • CPUレジスタとVRAMを復帰

実際の命令は下記のようになっています。この命令を実行させるために ROM image の NMI vector address は変更します。

   org    xxxx
;push CPU registers
    push    ax
    push    bx
    push    cx
    push    dx
    push    si
    push    di
    push    bp
    push    ds
;copy stack pointer
    mov    ax,nmiseg
    mov    bx,ramseg
    mov    ds,ax
    mov    [ssoffset],ss
    mov    [spoffset],sp
;copy sprite RAM
    mov    es,ax
    mov    ds,bx
    xor    si,si
    mov    di,dataoffset
    mov    cx,0400h>>1
    rep    movsw
;copy color RAM
    mov    si,8000h
    mov    cx,0c00h>>1
    rep    movsw
    mov    si,0c000h
    mov    cx,0c00h>>1
    rep    movsw
;sleep v30, wakeup TG68
    out    24h,al
    nop
    nop
    nop
;wakeup v30
    nop
    nop
    nop
    nop
;restore stack pointer
    mov    ds,ax
    mov    ss,[ssoffset]
    mov    sp,[spoffset]
;restore sprite RAM
    mov    es,bx
    mov    si,dataoffset
    xor    di,di
    mov    cx,0400h>>1
    rep    movsw
;restore color RAM
    mov    di,8000h
    mov    cx,0c00h>>1
    rep    movsw
    mov    di,0c000h
    mov    cx,0c00h>>1
    rep    movsw
;pop CPU registers
    pop    es
    pop    ds
    pop    bp
    pop    di
    pop    si
    pop    dx
    pop    cx
    pop    bx
    pop    ax
;    sti
    iret

NMI ルーチンは単純にしましたので、68000がRAMを書き換えなくても毎回内容を復帰することになります。
scroll register と scanline interrupt register は本来 write only なので read できるようにして RAM へ dump することも考えましたが、M72 の特性上、1フレーム当たり2度書き換えるものが大半ですから、ソフトで毎フレーム書き換えると判断しました。よってこの2つの register は state save の対象外です。

実際の動作について

手間取るかと思いきや1発でちゃんと動きました。今回の開発で1発で成功はかなり珍しかったです。

NMI の発生タイミングと復帰タイミングは vblank 割り込みがかかる直前にかけてあります。実際に動かしたところをみるとそこまで厳密にやらなくてもちゃんと動いているみたいです。

やはりサウンドが未対応なので、死んでしまって曲が停まったところで load をすると曲が停まったままゲームが再開してしまいます。かかっていた曲番号を記憶して、再生し直す実装をいれたほうがいいかもしれません。

あとは.... 実CPUとはいえこの手のPCで動くエミュレータぽい機能を入れると、それとの違いがわからなくなります。NMI の実用実験を兼ねて入れましたが、本来はない方がいいかもしれません。(デバッグには便利です)

  • 最終更新:2014-12-30 00:13:12

このWIKIを編集するにはパスワード入力が必要です

認証パスワード