なんも説明もしてなかった気がするので

とりあえず少し書いておくと、まず第1回で書いたようにROMは作れた。

A0 0 0
A1 0 0
A2 0 0
...
Ae 0 0
Af 1 1
## こんな感じにADDR <- 0x7FFF みたいに、アドレスをセットして
## ROMのコードに突っ込むと

## BEGIN ROM
if (ADDR-- == 0) {
  DATA <- 'H';
}
if (ADDR-- == 0) {
  DATA <- 'e';
}
if (ADDR-- == 0) {
  DATA <- 'l';
}
...
if (ADDR-- == 0) {
  DATA <- '\n';
}
## END ROM

## ここで DATA に対応するバイトが読まれてる

次にCPUを作る。

PC <- 0x0000 # プログラムカウンタを0に
# リセット完了
LOOP1:
LOOP2:
ADDR <- PC   # プログラムカウンタをアドレスレジスタにコピー
ROMを実行する
DATAをデコードする
PC++
なんか実行する
if (出力命令でもHLT命令でもない) goto LOOP1;
出力する
if (HLT命令じゃない) goto LOOP2;

大枠はこんな感じ。あとは命令セットを決めて実装すればよい。Quineを作るだけなので簡単な命令だけでよくて

// 8bitレジスタ  A
// 16bitレジスタ HL
// 16bitレジスタ SAVED_PC
op = read_rom(PC);
if (op & 0x80) {
  if (op & 0x40) {
    oprand, eom = read_rom(HL); // オペランドをフェッチする。HLがメモリの終端を指してたらeom=1
    if (op & 0x20) {
      // INC_HL_AND_JUMP_UNLESS_EOM
      HL++;
      if (!eom) {
        PC = SAVED_PC;
      }
    } else {
      // LD A, (HL)
      A = oprand;
    }
  } else {
    if (op & 0x20) {
      // SHIFT_PUTC 命令
      putchar('0' + (A & 1));
      A >>= 1;
    } else {
      if (op & 0x10) {
        // HLT 命令
        hlt();
      } else {
        // SAVE_PC命令
        SAVED_PC = PC;
      }
    }
  }
} else {
  // PUTC 命令
  putchar(op & 0x7F);
}

というルールでデコードして実行する。オペランドをフェッチするところをCPUに足すのは簡単にできるからこれで完成。
定数文字を出力する命令が一番多いので、7ビット目が立ってない命令はそのまま出力にしてある。