typedef struct rfcstate_object {
#undef KS_DEBUG

#ifndef KS_DEBUG
#undef ksPrintf
#define ksPrintf #
#else
#ifndef ksPrintf
#define ksPrintf osSyncPrintf
#endif
#endif

  _64 u8 wram[0x800];
  _64 void *jumptbl[0x100 * 4]; /* sd 各命令のジャンプテーブル */
/* store_ppu_functbl から store_mmc5_functbl までの関数ポインタ群は 初期設定テーブルがあるので 並び替えには注意する !!
 * 初期化時 sd する。
 */
  _64 void *store_ppu_functbl[8]; /* 0x2000..0x2007 storefunc */
  _64 void *store_io_functbl[0x28]; /* 0x4000..0x4026, alignment のため 0x28 個確保 */
  _64 void *storefunc_tbl[0x12000 / 0x2000]; /* storefunc ポインタ */
  void *loadfunc_tbl[0x12000 / 0x2000]; /* (sd) emu-cpu addr 0x0000-, 0x2000 bytes おきの loadfunc ポインタ */
  _64 void *load_io_functbl[0x18]; /* 0x4000..0x4017 */
  _64 void *mmc5_storefunc_tbl[0x34]; /* sd. 0x5100..0x5130 + aligner */
  _64 u8 *page_table[0x10000 / 0x2000]; /* 0x0000-, 0x2000-, ..., 0xe000- の page table で PC を加えて実アドレスになる ただし 0x6000- 以降のエントリのみ使用 */
  _64 u8 ppu2000h_output[8]; /* sd. emu-cpu の読み出し用。基本的に 0x20 で埋めておく。[2] は d0? を vram address latch order としても使う */
  _64 u8 ppu2000h[8]; /* sd */

  _32 u8 qd4030h[4]; /* 0x4030-0x4033 emu-cpu 読み出し用 */
  int emuframe;
  u8 *headerp; /* extrom の指す adderss - 0x10 */
  u8 *prgrom, *chrrom;
  u32 prgsize, chrsize; /* chrram のときは chrsize が 0 になる */
  u32 chr_offset_mask; /* chrrom/ram address mask */
  u8 qd4025h_release, qd4025h_trigger, qd4025h_bak, qd_dirty;
  u32 mmc1_bigrom;
u32 dummy3[1];
_32  u8 qd_info_block[8]; /* game code 4 bytes, ver, side, volume, reserved */
  u8 *qdimage; /* prgrom の手前に配置。0x10000 * 2 sides * 2 disks max */
_128  u8 *ucode_workp;
  u32 qdoffset; /* lhu/sh でも使う !!
                 * 0x00000 = disk1 sideA, 0x10000 = disk1 sideB,
                 * 0x20000 = disk2 sideA, 0x30000 = disk2 sideB
                 * (0x40000 = dummy data を返すために使う)
                 */
  u8 qd4020h[8]; /* 4020,4021 は 逆で lhu する。for write from emu-cpu */
_128  u8 *gfxptr;
  void *chrbank_funcp; /* H-blank ごとの chr bank 設定関数ポインタ */
  void *linecnt_over_funcp; /* ラインカウンタオーバー時 処理関数ポインタ */

  s32 thread_store_done, thread_jumptblp, thread_page_ptr, thread_linecycle;
  u32 work_rspsignal;
  /* SP がデータコピーを終わったかどうかを CPU が polling するが、
   * 最初の 1 回だけは polling しないよう SP_STATUS_RSPSIGNAL を入れる。
   * SP が動き出す 2 回目以降は 0 を入れる。
   */
  u32 thread_pc, thread_ra, thread_gp;
  u32 trace_flag; /* デバッグ用ワーク */
  s32 soundcycle;
  _32 u8 mmc3_r0; /* 初期化時 sw する. r6 まで byte 単位で連続している */
  u8 mmc3_r1, mmc3_r2, mmc3_r3, mmc3_r4, mmc3_r5;
  _16 u8 mmc3_r6; /* 初期化時 sh する */
  u8 mmc3_r7;
  u8 mmc3_pointer;
  u8 strikef_line;
  u8 sun5_cmd;
  u8 sun5_irq_l, sun5_irq_h;
  u8 vrc4_irq_l, vrc4_irq_h;
  u8 vrc4_mapmode, vrc4_pbank;
  u8 vrc6_irq, vrc6_irq_select, vrc6_reg_swap, vrc6_irq_reg;
  u8 linecnt_irq_enabled; /* 0 でなければ ラインカウンタオーバー割り込み発生 */
  u8 irq_cause; /* d7: deltemod(未実装), d6: V ext mode, d5: linecnt */
  u8 mmc1_shiftcount, mmc1_shiftreg; /* 5..1,0 shiftreg は 作業用 */
  u8 mmc1_r0, mmc1_r1, mmc1_r2, mmc1_r3, mmc1_rombank, mmc1_rambank; /* r0-r3 は連続している */
  u8 mmc1_ignore_chrbank; /* chrram, bigrom のときに chrbank の変更を無視する */
  u8 mmc1_dq4_mode;
  u8 mmc5_mult_src;
  u8 mmc5_br3, mmc5_br4, mmc5_br5, mmc5_br6, mmc5_br7;
  u8 force_media_sense_flag; /* 0 なら マニュアルで QD eject 処理
                              * 1 以上ならカウントアップ、255->0 で終了。
                              * 1-70: 強制 QD eject 状態
                              * 71-255: 強制 QD set 状態
                              */
  u16 mmc5_amode_pamode; /* 上位 byte が amode */
  u16 mmc5_pbrex;
  u16 ss_pbank0[3]; /* Jaleco SS8806 pbank l, h for 8000-,a000-,c000- */
  u16 ss_chrbank0[8]; /* chrbank l, h for 0000-,0400-, ... */
  u16 ss_irql, ss_irqh; /* irq counter, low byte-l, h; high byte-l, h; かな ? 全部 or で最終値が 求まるように。*/
  s16 vrc4_linecnt_save; /* disksys の linecnt save でも使う */
  u16 chrseg_for_read; /* 0 or 0x1000 で、chrrom 読み出し時に ppu_addr に xor する */
  s16 linecnt; /* -22..1..-1,0,..239,240(H blank 後 -22) の H blank 終了までが emuframe */
  s16 linecnt_irq; /* linecnt と等しくなったら linecnt_irq_preset の値でリセット. (disksys では、その値を今の linecnt へ加える) 必要なら IRQ 発生 */
  u16 linecnt_irq_preset; /* 0 なら単発。そうでなければ linecnt_irq に加える */
  u16 ppu_addr;
  u16 vram_mirror_mask_and, vram_mirror_mask_or;
  u16 draw_sccv, draw_sccv_new; /* フレーム開始時 draw_sccv_new から draw_sccv へ */
  u16 pc;
  u16 work_4010h_output; /* 4010H に store した data. リセット後 0xffff */
  u8 a,x,y,sp, work_z, work_c, work_v, work_n, work_rbdi; /* work_z は P レジスタの Z (d7-d0=all 0 なら zero) だけを持つ。 Carry, overflow, Negative, Reserved|B|D|I も分ける。*/
  u8 pbank_mask;   /* 0x2000 bytes 刻みの bank mask */
  u8 gfx_selbuf; /* 0 or 1 */
  u8 gfx_selbuf_next; /* 倍速のとき、asm ルーチン内で gfx_selbuf を toggle しないように */
  u8 palette_change_count;
  u8 bg_chrbank_touch_flag;
  u8 obj_chrbank_touch_flag;
  u8 draw_bg_chrbank_count;  /* 現在描画に使う chr bank count */
  u8 bg_chrbank_change_count; /* count max */
  u8 draw_obj_chrbank_count;
  u8 obj_chrbank_change_count;
  u8 draw_scch;
  u8 sun4, sun4_vrom_enabled, sun4_vrom0, sun4_vrom1; /* vrom0,1 はつながっている */
  /* Sunsoft 4 かどうか (0 or 0x40). screen を chrrom に持つなら 0x40, 通常は 0.
   * left, right の vrom bank.
   */
  u8 oam_change_count;
  u8 work_4015h_output; /* 4015H emu-cpu 読み出し用 */
  u8 work_4016h; /* 4016H に store した data */
  u8 work_4017h; /* 4017H に store した data */
  u8 sound_cycle_divider;
  u8 pc_hist_offset; /* 下の pc_hist_buf の byte offset (2 単位) */

  u64 force_structure_alignment[1];
  u16 chrbank_reg0000; /* ld する */
  u16 chrbank_reg0400;
  u16 chrbank_reg0800;
  u16 chrbank_reg0c00;
  u16 chrbank_reg1000;
  u16 chrbank_reg1400;
  u16 chrbank_reg1800;
  u16 chrbank_reg1c00;
  _32 u16 mmc5_pbr0[8 + 8]; /* lw. pbr0..7, pbr4'..7', 残り 処理用ダミー 4 個 */
  _64 u8 mmc5_exram[0x400 + 0x08]; /* 5c00-5fff と 5200-5203,5205,5206 read 分 */
  _64 u8 oam[0x100];
  _64 u8 bbram[0x2000]; /* OFFSET_BBRAM - 0x6000 の形で使うので OFFSET_BBRAM < 0xe000 でよい */
  _64 u8 trainer[0x200]; /* address 0x7000..0x71ff だよね。0x5000..0x51ff ? (seinto.nes) */
  _64 u8 palette_buf[0x20];
  _64 u32 serial4016d1; /* sd シリアル初期化時 work_4016d1 などへ copy する */
      u32 serial4016d0;
      u32 serial4017d5, serial4017d4, serial4017d3, serial4017d2, serial4017d1, serial4017d0; /* d5 は ld/sd のためのダミー */
  _64 u32 work_4016d1;
      u32 work_4016d0;   /* ld/sd */
      u32 work_4017d5, work_4017d4, work_4017d3, work_4017d2, work_4017d1, work_4017d0; /* d5 は ld/sd のためのダミー */

/*
 * gfxbuf は CPU からは gfxbuf へ書き込み、SP は最初に gfxbuf -> ucode_wk へ DMA copy する。
 * OFFSET_GFXBUF_      OFFSET_UCODEWK_
 *                   |
 *                   | ----------
 *                   |   objchr 0
 *                   |   objchr 1
 *  ---------------  | ----------
 *    draw list      |
 *    -------------  |
 *    oam 0          |
 *    -------------  |
 *    oam 1          |
 *    -------------  |
 *    obj chrbank tbl|
 *    -------------  |
 *    bg chrbank tbl |
 *    -------------  |
 *    palette 0-7    |
 *  ---------------  |
 *    draw vram      |
 *  ---------------  |
 *    chr touch flag |
 *  ---------------  |
 *    draw chr       |
 *  ---------------  | ------------
 *                   |   line index
 *                   | ------------
 * という構成になっている。
 *
 * objchr は 8x16 モードのことを考えて 0x20 * 0x40 objs.
 * palette は 8 changes/frame Max. oam は 2 buffers Max.
 * chr touch flag は 0x2000 bytes 単位. chrrom は 0x80000 bytes Max. なので
 *   0x40 bytes.
 * draw vram が CPU から見てシングルバッファだが、touch flag を別に持っている。
 * chr touch flag も 別の場所に work chr touch flag を持っていて、
 * draw list を swap するタイミングで draw vram, chr touch flag も update する。
 * chr touch flag の情報をもとに SP は
 *   CPU から見た draw chr -> SP から見た draw chr へ DMA copy する。
 * 
 * line index は 256 bytes/line で 1dot=1byte.
 * 詳細は | 0 | OBJ protected | BG/OBJ | attr1-0 | dot data1-0 | 0 |
 *  の 8bit で、OBJ protected が 1 なら もう OBJ の上書きはできない。
 *  BG/OBJ は どっちのパレットか。 0=BG
 * 
 * draw list は 8 bytes/line で 240 lines 分。
 * 8 bytes の詳細は
 * DRAW_2000H
 * DRAW_2001H
 * NEXT_LINE
 * DRAW_BG_BANK
 * DRAW_VRAM_ADDR
 * DRAW_SCCH
 * (余り)
 * で、
 * DRAW_2001H, DRAW_2000H: そのまま。
 * NEXT_LINE: 最初以外の entry で 0 なら、それ以降同じ draw bg bank の line
 *            はない。
 *            0 以外なら、+ そのライン数 後に同じ draw bg bank の line.
 *            ラスターとかしなければ、ここには 8 が入ることが多い。
 * DRAW_SCCH: SCCH low 8bit.
 *
 * DRAW_BG_BANK: | blanking | chr bank table idx 3-0 | line 2-0 |
 *               の 8bit で、chr bank table というのは 8bytes/entry で
 *     | bank0-H,L | bank1-H,L | bank2-H,L | bank3-H,L |
 *     のようになっている。bank0-3 は chr.no 0x00-,0x40-,0x80-,0xc0- の
 *     chr bank である。PPU 2000H の BG chr seg. も含んでいる。
 *
 * DRAW_VRAM_ADDR: | wrap | vram page | addr5-0 | の 8bit で、
 *                 wrap=0 なら 左も右も同じ vram page, 1 なら 別の page.
 *                 PPU 2000H の MSBV, MSBH, 縦持ち/横持ち を含んでいる。
 *                 addr5-0 は draw vram 0x40 bytes (1 行) 単位のアドレス。
 *                 draw vram data は real data 0x20 bytes + attr data 0x20 bytes.
 *                 attr data は | attr 1,0 | (reserved bank5-0) | の 8bit.
 *
 * 以下は未実装。
 * obj chr bank table への index, palette index, oam index
 * bg chr bank table は bg chr 0x00-,0x40-,0x80-,0xc0- の chr bank で
 * 2 * 4 bytes 一組。
 * obj chr bank table は 0x000-,0x040-,0x080-,...,0x1c0- の chr bank で
 * 2 * 8 bytes 一組。
 */

#define BG_BANK_CHANGE_COUNT_MAX 16
#define SIZEOF_BG_CHRBANK_TBL    ((BG_BANK_CHANGE_COUNT_MAX+1)*2*4)
#define OBJ_BANK_CHANGE_COUNT_MAX 8
#define SIZEOF_OBJ_CHRBANK_TBL   ((OBJ_BANK_CHANGE_COUNT_MAX+1)*2*8)
#define SIZEOF_DRAW_LIST         (8*240)
#define PALETTE_CHANGE_COUNT_MAX 8
#define SIZEOF_DRAW_PALETTE_BUF  (0x20*(PALETTE_CHANGE_COUNT_MAX+1))
#define SIZEOF_OAM               0x100
#define SIZEOF_OBJCHR            (0x20*0x40)
/*
 * draw vram の前半と共通で、後半は描画のみに使用。
 * 
 */
#define KS_RFC_VRAM_SIZE         0x800
#define SIZEOF_DRAW_VRAM         (KS_RFC_VRAM_SIZE*2)
#define SIZEOF_DRAW_AT_VRAM      0x400
#define SIZEOF_CHR_TOUCH_FLAG    (SIZEOF_DRAW_CHR/0x2000)
/* SIZEOF_DRAW_CHR は 0x10000 単位にするように。
 * aftrbrn2.nes の対策で 0x40000 + 0x40000 ないといけない。
 * 110-in-1.nes が 0x100000 bytes.
 * 動物の森バージョン は 0x10000 に。
 */
#if 01
#define SIZEOF_DRAW_CHR          0x10000 /* 動物の森バージョンデフォルト */
#else
#define SIZEOF_DRAW_CHR          0x40000 /* TEST by HAYAKAWA 4MB で足りないときは元に戻す */
#endif
/* LINE_INDEX_BUF は実際には obj 処理のマージンとして 上に 0x10 lines, 下に 0x10 lines 余計に確保される */
#define SIZEOF_LINE_INDEX_BUF    (256*240)

/* asmrfc.s で 順序依存してるので注意。
 * Sunsoft 4 では draw vram addr の d6=1 になり、そのとき
 * draw 2000h: d7, d6, -, -, -, d2,d1,d0
 * draw 2001h:        d7,d6,d5
 * を draw vrom left (8bits) として使う。どうせ全画面着色してないでしょ。
 * -> あかん。chrbank 0xff まで必要だ。
 */
#define OFFSET_GFX_DRAW_2000H     0
#define OFFSET_GFX_DRAW_2001H     1
#define OFFSET_GFX_NEXT_LINE      2
#define OFFSET_GFX_DRAW_SCCH      3
#define OFFSET_GFX_DRAW_VRAM_ADDR 4
#define OFFSET_GFX_DRAW_BG_BANK   5
#define OFFSET_GFX_DRAW_OBJ_BANK  6
#define OFFSET_GFX_DRAW_VROM_R    7

#define OFFSET_GFXBUF_DRAW_LIST   0
#define OFFSET_GFXBUF_OAM0        (OFFSET_GFXBUF_DRAW_LIST+SIZEOF_DRAW_LIST)
#define OFFSET_GFXBUF_OAM1        (OFFSET_GFXBUF_OAM0+SIZEOF_OAM)
#define OFFSET_GFXBUF_OBJ_CHRBANK_TBL (OFFSET_GFXBUF_OAM1+SIZEOF_OAM)
#define OFFSET_GFXBUF_BG_CHRBANK_TBL  (OFFSET_GFXBUF_OBJ_CHRBANK_TBL+SIZEOF_OBJ_CHRBANK_TBL)
#define OFFSET_GFXBUF_PALETTE0    (OFFSET_GFXBUF_BG_CHRBANK_TBL+SIZEOF_BG_CHRBANK_TBL+8) /* 16bytes alignment にするため */
#define OFFSET_GFXBUF_DRAW_VRAM   (OFFSET_GFXBUF_PALETTE0+SIZEOF_DRAW_PALETTE_BUF)
#define OFFSET_GFXBUF_CHR_TOUCH_FLAG (OFFSET_GFXBUF_DRAW_VRAM+SIZEOF_DRAW_VRAM+SIZEOF_DRAW_AT_VRAM)
#define OFFSET_GFXBUF_DRAW_CHR    (OFFSET_GFXBUF_CHR_TOUCH_FLAG+SIZEOF_CHR_TOUCH_FLAG)
#define SIZEOF_GFXBUF             (OFFSET_GFXBUF_DRAW_CHR+SIZEOF_DRAW_CHR)
#define SIZEOF_GFXBUF2            OFFSET_GFXBUF_DRAW_VRAM

#define OFFSET_UCODEWK_OBJCHR0    0
#define OFFSET_UCODEWK_OBJCHR1    (OFFSET_UCODEWK_OBJCHR0+SIZEOF_OBJCHR)
#define OFFSET_UCODEWK_DRAW_VRAM  (OFFSET_UCODEWK_OBJCHR1+SIZEOF_OBJCHR)
#define OFFSET_UCODEWK_CHR_TOUCH_FLAG (OFFSET_UCODEWK_DRAW_VRAM+SIZEOF_DRAW_VRAM)
#define OFFSET_UCODEWK_DRAW_CHR   (OFFSET_UCODEWK_CHR_TOUCH_FLAG+SIZEOF_CHR_TOUCH_FLAG)
#define OFFSET_UCODEWK_LINE_INDEX (OFFSET_UCODEWK_DRAW_CHR+SIZEOF_DRAW_CHR+256*0x10) /* obj 処理のマージン */
#define SIZEOF_UCODEWK            (OFFSET_UCODEWK_LINE_INDEX+SIZEOF_LINE_INDEX_BUF+256*0x30) /* obj 処理のマージン */
/* state backup/restore の都合で、上の SIZEOF_UCODEWK は 256*0x10 じゃなくて 0x30 ぐらいにしてみた。
 * 0x10 では足らないみたいだ。
 */
  _64 u8 gfxbuf [SIZEOF_GFXBUF];
  _64 u8 gfxbuf2[SIZEOF_GFXBUF2]; /* draw list, oam, chrbank table, palette は ダブルバッファ */
  u16 pc_hist_buf[0x80]; /* デバッグ用 PC history バッファ offset > 0x7fff でよい。*/

} rfcstate_object;
