Posts Issued in December, 2025

GameFSMの改良 (19)

posted by sakurai on December 1, 2025 #1044

ブリッジのコードに怪しいところがあるので、修正中です。さて、オープニング画面を追加したいと思い、bsvコードを書きました。8bitレトロマシンとは思えないくらいの立体感のある映像になりました。

まず画像のロード関数です。元画像はここにあった改造度の高いものを256x256に縮め、さらに色数を4色に落としました。パターンROMにおいて、VRAMに転送するだけですが、ただ転送するのはあまり面白くないので、RLE (run length encoding)を行いました。

元画像はpythonでRLEしましたが、そのデコーダのbsvコードを示します。

// PROM アドレス(1 nibble 単位)
Reg#(PAddr_t)   rleAddr      <- mkReg(0);

// 展開先の相対座標 (0..255, 0..255)
Reg#(U8)        rleX         <- mkReg(0);
Reg#(U8)        rleY         <- mkReg(0);

// 残りピクセル数 256 * 256
Reg#(UInt#(16)) pixelsLeft   <- mkReg(0);

// 現在のランの残り長さ (1..256)
Reg#(UInt#(9))  runLen       <- mkReg(0);

// 色と長さ nibble
Reg#(Pattern_t) rleColor     <- mkReg(0);
Reg#(Pattern_t) rleLenHiNib  <- mkReg(0);
Reg#(Pattern_t) rleLenLoNib  <- mkReg(0);

// PROM から nibble(Pattern_t) を1つ読み取り、dst に入れ、rleAddr を 1 進める
function Stmt rleNextNibble(Reg#(Pattern_t) dst);
  return (seq
    action
      // PROM アドレスをセット
      p_addr <= rleAddr;
    endaction
    // read timing 調整が必要ならここに noAction を挟む
    noAction;
    action
      // nibble を取り込み
      dst     <= romdata;      // romdata : Pattern_t
      rleAddr <= rleAddr + 1;  // 次の nibble へ
    endaction
  endseq);
endfunction

// PROM 上で RLE が始まるニブルアドレス(行256の先頭なら 128*256 など、今は簡易に 256)
`define RLE_START_ADDR 128*256
`define VRAM_WIDTH     256
`define VRAM_HEIGHT    256
   
function Stmt rleDecode();
  return (seq
    //------------------------------------------------
    // 初期化
    //------------------------------------------------
    action
      // RLE データは PROM の 256 行目から始まる前提
      rleAddr    <= fromInteger(`RLE_START_ADDR);
      // 出力先相対座標
      rleX       <= 0;
      rleY       <= 0;
      pixelsLeft <= fromInteger(`VRAM_WIDTH * `VRAM_HEIGHT -1);
      runLen     <= 0;
    endaction
    //------------------------------------------------
    // 全ピクセルを描き終えるまでループ
    //------------------------------------------------
    while (pixelsLeft != 0) seq
      // --- 新しいランを読み込む必要があるなら ---
      if (runLen == 0) seq
        // color
        rleNextNibble(rleColor);
        // len_hi
        rleNextNibble(rleLenHiNib);
        // len_lo
        rleNextNibble(rleLenLoNib);
        action
          UInt#(4) hi = unpack(rleLenHiNib);
          UInt#(4) lo = unpack(rleLenLoNib);
          // ラン長 L = 16*hi + lo + 1
          runLen <= extend(unpack({pack(hi), pack(lo)})) + 1;
        endaction
      endseq
      // --- このランから 1 ピクセル描画 ---
      setDot(rleX, rleY, rleColor);
      action
        pixelsLeft <= pixelsLeft - 1;
        runLen     <= runLen     - 1;
        // 128 幅で折り返し
        if (rleX == 8'd255) begin
          rleX <= 0;
          rleY <= rleY + 1;
        end
        else begin
          rleX <= rleX + 1;
        end
      endaction
    endseq   // while (pixelsLeft != 0)
  endseq);
endfunction
FSM rle_fsm <- mkFSM(rleDecode());

function Stmt runRle();
  return (seq
    `RUN_FSM(rle_fsm)
  endseq);
endfunction

左矢前のブログ 次のブログ右矢