/* -*- asm -*- */

/* implement 中
 * store05_io
 *
 * implement が必要なルーチン
 * load05_io
 * load05_5204, mmc5_5204_output
 */
		
#include <asm.h>
#include <regdef.h>
#include <PR/R4300.h>
#include "offset-rfc.inc"

/* asmrfc.s のレジスタ割り当て。ni_debug.c で参照できるように ks_rfc.inc
 * でやるのも良いが、マクロ名が短すぎて重複の危険があるので やめた。
 * フラグレジスタの Reserved|B|D|I は メモリに持つのみ
 * JUMPTBLP は 今処理中のジャンプテーブルの先頭番地 - OFFSET_JUMPTBL
 * STORE_DONE は命令の処理開始のときに next が入る。
 * PAGE_PTR は loadfunc 下請けなどで使う。
 * Z は 0xff を越えることもあるので 0 かどうかの比較は andi 0xff をしてから。
 * C は 必ず 0 or 1
 * N は 必ず 0 or 0x80
 * SP は 0x00..0xff で、読み書きのとき OFFSET_WRAM + 0x100(xx) とする。
 */
	
#define WORK fp
#define A    s0
#define X    s1
#define Y    s2
#define PC   s3
#define SP   s4
#define Z    s5
#define C    s6
#define V    s7
#define N    gp


/* push_v1 を読んでいるのでとりあえず STORE_DONE は v1 にしないように。
 * STORE_DONE は t6 にした。t6 はワークで使っているところもあるがセーフ。
 * store_sound でこれらを save しているところがあるので注意。
 */
#define STORE_DONE  t6
#define JUMPTBLP    t7
#define PAGE_PTR    t8
#define LINECYCLE   t9

#ifndef KS_DEBUG
#undef KS_TRACE
#undef KS_HISTORY
#undef KS_DEBUG_REGS
#undef KS_TRAP_STORE_INVALID
#endif
	
/* R4300 stack frame offset
 * スタックフレームは 8 bytes alignment 
 * OFFSET_STACK_CALLER_A[0-3] は呼ばれたときの引数の save 領域
 * C コンパイラのお約束では a0-a3 は EMU_PGB_ASM_STACKSIZE+0x00 からでよいが
 * そこは使わない。別に使わなくったっていいのだ。
 * 引数 8 int の関数を呼ぶとして、+8 bytes 余裕をみて、先頭の 40 bytes は reserved
 */
#define EMU_RFC_ASM_STACKSIZE 0x100
#define OFFSET_STACK_CALLER_A0 0x28
#define OFFSET_STACK_CALLER_A1 0x2c
#define OFFSET_STACK_CALLER_A2 0x30
#define OFFSET_STACK_CALLER_A3 0x34

#define OFFSET_STACK_S0 0x40
#define OFFSET_STACK_S1 0x44
#define OFFSET_STACK_S2 0x48
#define OFFSET_STACK_S3 0x4c
#define OFFSET_STACK_S4 0x50
#define OFFSET_STACK_S5 0x54
#define OFFSET_STACK_S6 0x58
#define OFFSET_STACK_S7 0x5c
#define OFFSET_STACK_GP 0x60
#define OFFSET_STACK_FP 0x64
#define OFFSET_STACK_RA 0x68

#define OFFSET_STACK_T0 0x6c
#define OFFSET_STACK_T1 0x70
#define OFFSET_STACK_T2 0x74
#define OFFSET_STACK_T3 0x78
#define OFFSET_STACK_T4 0x7c
#define OFFSET_STACK_T5 0x80
#define OFFSET_STACK_T6 0x84
#define OFFSET_STACK_T7 0x88
#define OFFSET_STACK_T8 0x8c
#define OFFSET_STACK_T9 0x90
#define OFFSET_STACK_A0 0x94
#define OFFSET_STACK_A1 0x98
#define OFFSET_STACK_A2 0x9c
#define OFFSET_STACK_A3 0xa0
#define OFFSET_STACK_V0 0xa4
#define OFFSET_STACK_V1 0xa8
#define OFFSET_STACK_RA_SOUND 0xac

#define OFFSET_STACK_T6_PRINTF  0xb0
#define OFFSET_STACK_T7_PRINTF  0xb4
#define OFFSET_STACK_T8_PRINTF  0xb8
#define OFFSET_STACK_T9_PRINTF  0xbc
#define OFFSET_STACK_V0_PRINTF  0xc0
#define OFFSET_STACK_V1_PRINTF  0xc4
	
#define OFFSET_STACK_RA_PRINTF  0xc8
#define OFFSET_STACK_AT_PRINTF  0xcc
#define OFFSET_STACK_RA_PRINTF2 0xd0
#define OFFSET_STACK_GP_PRINTF  0xd4
#define OFFSET_STACK_FP_PRINTF  0xd8
#define OFFSET_STACK_COUNTER    0xdc /* debug 用 */
#define OFFSET_STACK_DEBUG_V0   0xe0 /* debug 用 */
#define OFFSET_STACK_DEBUG_REGS 0xe4 /* debug 用 */



#undef PRINTF
/*#define KS_DEBUG_VERBOSE*/
#ifdef KS_DEBUG_VERBOSE
#define COUNTER_BEGIN \
	.set	noreorder; \
	mfc0	$1, C0_COUNT; \
	sw	$1, OFFSET_STACK_COUNTER(sp); \
	.set	reorder

#define COUNTER_END \
	.set	noreorder; \
	mfc0	$1, C0_COUNT; \
	lw	v0, OFFSET_STACK_COUNTER(sp); \
	subu	v0, $1, v0; \
	.set	reorder
	
#define TRAP(msg) \
	.data; \
206:	.asciiz	msg; \
	.text; \
	.set	noreorder; \
	la	k0, 206b; \
	lui	k1, 0x8000; \
	sw	k0, 0(k1); \
	teq	zero, zero; \
	nop; \
	.set	reorder

/* わざわざ move $1,a0; とかしてるのは
 * 直接 la $1, 205b; とかすると as が
 * Warning: macro instruction used $at といってうるさいから。
 */
#define PRINTF(f) \
	.data; \
205:	.asciiz	f; \
	.text; \
	.set noreorder; \
	sw	$1, OFFSET_STACK_AT_PRINTF(sp); \
	move $1,a0; \
	sw	ra, OFFSET_STACK_RA_PRINTF(sp); \
	la	a0, 205b; \
	jal	save_ossync_restore; \
	sw	$1, OFFSET_STACK_A0(sp); \
	.set reorder
#undef GLOBAL()
#define GLOBAL(s) .globl s; .aent s
#else
#define COUNTER_BEGIN
#define COUNTER_END
#define TRAP(msg) /* msg */
#define PRINTF(f) /* f */
#define GLOBAL(s) /* s */
#endif

#ifdef KS_DEBUG_VERBOSE
	.data
	.align	4
static_debug_insttbl:
	.asciiz "brk        "
1:	.asciiz "ora %02x,x   "
2:	.asciiz "?02"
3:	.asciiz "?03"
4:	.asciiz "?04"
5:	.asciiz "ora %02x     "
6:	.asciiz "asl %02x     "
7:	.asciiz "?07"
8:	.asciiz "php        "
9:	.asciiz "ora #%02x    "
10:	.asciiz "asl        "
11:	.asciiz "?0b"
12:	.asciiz "?0c"
13:	.asciiz "ora %04x   "
14:	.asciiz "asl %04x   "
15:	.asciiz "?0f"
16:	.asciiz "bpl        "
17:	.asciiz "ora (%02x),y "
18:	.asciiz "?12"
19:	.asciiz "?13"
20:	.asciiz "?14"
21:	.asciiz "ora %02x,x   "
22:	.asciiz "asl %02x,x   "
23:	.asciiz "?17"
24:	.asciiz "clc        "
25:	.asciiz "ora %04x,y "
26:	.asciiz "?1a"
27:	.asciiz "?1b"
28:	.asciiz "?1c"
29:	.asciiz "ora %04x,x "
30:	.asciiz "asl %04x,x "
31:	.asciiz "?1f"
32:	.asciiz "jsr %04x   "
33:	.asciiz "and (%02x,x) "
34:	.asciiz "?22"
35:	.asciiz "?23"
36:	.asciiz "bit %02x     "
37:	.asciiz "and %02x     "
38:	.asciiz "rol %02x     "
39:	.asciiz "?27"
40:	.asciiz "plp        "
41:	.asciiz "and #%02x    "
42:	.asciiz "rol        "
43:	.asciiz "?2b"
44:	.asciiz "bit %04x   "
45:	.asciiz "and %04x   "
46:	.asciiz "rol %04x   "
47:	.asciiz "?2f"
48:	.asciiz "bmi        "
49:	.asciiz "and (%02x),y "
50:	.asciiz "?32"
51:	.asciiz "?33"
52:	.asciiz "?34"
53:	.asciiz "and %02x,x   "
54:	.asciiz "rol %02x,x   "
55:	.asciiz "?37"
56:	.asciiz "sec        "
57:	.asciiz "and %04x,y "
58:	.asciiz "?3a        "
59:	.asciiz "?3b"
60:	.asciiz "?3c"
61:	.asciiz "and %04x,x "
62:	.asciiz "rol %04x,x "
63:	.asciiz "?3f"
64:	.asciiz "rti        "
65:	.asciiz "eor (%02x,x) "
66:	.asciiz "?42        "
67:	.asciiz "?43"
68:	.asciiz "?44"
69:	.asciiz "eor %02x     "
70:	.asciiz "lsr %02x     "
71:	.asciiz "?47"
72:	.asciiz "pha        "
73:	.asciiz "eor #%02x    "
74:	.asciiz "lsr        "
75:	.asciiz "?4b"
76:	.asciiz "jmp %04x   "
77:	.asciiz "eor %04x   "
78:	.asciiz "lsr %04x   "
79:	.asciiz "?4f"
80:	.asciiz "bvc        "
81:	.asciiz "eor (%02x),y "
82:	.asciiz "?52"
83:	.asciiz "?53"
84:	.asciiz "?54"
85:	.asciiz "eor %02x,x   "
86:	.asciiz "lsr %02x,x   "
87:	.asciiz "?57"
88:	.asciiz "cli        "
89:	.asciiz "eor %04x,y "
90:	.asciiz "?5a"
91:	.asciiz "?5b"
92:	.asciiz "?5c"
93:	.asciiz "eor %04x,x "
94:	.asciiz "lsr %04x,x "
95:	.asciiz "?5f"
96:	.asciiz "rts        "
97:	.asciiz "adc (%02x,x) "
98:	.asciiz "?62"
99:	.asciiz "?63"
100:	.asciiz "?64"
101:	.asciiz "adc %02x     "
102:	.asciiz "ror %02x     "
103:	.asciiz "?67"
104:	.asciiz "pla        "
105:	.asciiz "adc #%02x    "
106:	.asciiz "ror        "
107:	.asciiz "?6b"
108:	.asciiz "jmp (%04x) "
109:	.asciiz "adc %04x   "
110:	.asciiz "ror %04x   "
111:	.asciiz "?6f"
112:	.asciiz "bvs        "
113:	.asciiz "adc (%02x),y "
114:	.asciiz "?72"
115:	.asciiz "?73"
116:	.asciiz "?74"
117:	.asciiz "adc %02x,x   "
118:	.asciiz "ror %02x,x   "
119:	.asciiz "?77"
120:	.asciiz "sei        "
121:	.asciiz "adc %04x,y "
122:	.asciiz "?7a"
123:	.asciiz "?7b"
124:	.asciiz "?7c"
125:	.asciiz "adc %04x,x "
126:	.asciiz "ror %04x,x "
127:	.asciiz "?7f"
128:	.asciiz "?80"
129:	.asciiz "sta (%02x,x) "
130:	.asciiz "?82"
131:	.asciiz "?83"
132:	.asciiz "sty %02x     "
133:	.asciiz "sta %02x     "
134:	.asciiz "stx %02x     "
135:	.asciiz "?87"
136:	.asciiz "dey        "
137:	.asciiz "?89"
138:	.asciiz "txa        "
139:	.asciiz "?8b"
140:	.asciiz "sty %04x   "
141:	.asciiz "sta %04x   "
142:	.asciiz "stx %04x   "
143:	.asciiz "?8f"
144:	.asciiz "bcc        "
145:	.asciiz "sta (%02x),y "
146:	.asciiz "?92"
147:	.asciiz "?93"
148:	.asciiz "sty %02x,x   "
149:	.asciiz "sta %02x,x   "
150:	.asciiz "stx %02x,y   "
151:	.asciiz "?97"
152:	.asciiz "tya        "
153:	.asciiz "sta %04x,y "
154:	.asciiz "txs        "
155:	.asciiz "?9b"
156:	.asciiz "?9c"
157:	.asciiz "sta %04x,x "
158:	.asciiz "?9e"
159:	.asciiz "?9f"
160:	.asciiz "ldy #%02x    "
161:	.asciiz "lda (%02x,x) "
162:	.asciiz "ldx #%02x    "
163:	.asciiz "?a3"
164:	.asciiz "ldy %02x     "
165:	.asciiz "lda %02x     "
166:	.asciiz "ldx %02x     "
167:	.asciiz "?a7"
168:	.asciiz "tay        "
169:	.asciiz "lda #%02x    "
170:	.asciiz "tax        "
171:	.asciiz "?ab"
172:	.asciiz "ldy %04x   "
173:	.asciiz "lda %04x   "
174:	.asciiz "ldx %04x   "
175:	.asciiz "?af"
176:	.asciiz "bcs        "
177:	.asciiz "lda (%02x),y "
178:	.asciiz "?b2"
179:	.asciiz "?b3"
180:	.asciiz "ldy %02x,x   "
181:	.asciiz "lda %02x,x   "
182:	.asciiz "ldx %02x,y   "
183:	.asciiz "?b7"
184:	.asciiz "clv        "
185:	.asciiz "lda %04x,y "
186:	.asciiz "tsx        "
187:	.asciiz "?bb"
188:	.asciiz "ldy %04x,x "
189:	.asciiz "lda %04x,x "
190:	.asciiz "ldx %04x,y "
191:	.asciiz "?bf"
192:	.asciiz "cpy #%02x    "
193:	.asciiz "cmp (%02x,x) "
194:	.asciiz "?c2"
195:	.asciiz "?c3"
196:	.asciiz "cpy %02x     "
197:	.asciiz "cmp %02x     "
198:	.asciiz "dec        "
199:	.asciiz "?c7"
200:	.asciiz "iny        "
201:	.asciiz "cmp #%02x    "
202:	.asciiz "dex        "
203:	.asciiz "?cb"
204:	.asciiz "cpy %04x   "
205:	.asciiz "cmp %04x   "
206:	.asciiz "dec %04x   "
207:	.asciiz "?cf"
208:	.asciiz "bne        "
209:	.asciiz "cmp (%02x),y "
210:	.asciiz "?d2"
211:	.asciiz "?d3"
212:	.asciiz "?d4"
213:	.asciiz "cmp %02x,x   "
214:	.asciiz "dec %02x,x   "
215:	.asciiz "?d7"
216:	.asciiz "cld        "
217:	.asciiz "cmp %04x,y "
218:	.asciiz "?da"
219:	.asciiz "?db"
220:	.asciiz "?dc"
221:	.asciiz "cmp %04x,x "
222:	.asciiz "dec %04x,x "
223:	.asciiz "?df"
224:	.asciiz "cpx #%02x    "
225:	.asciiz "sbc (%02x,x) "
226:	.asciiz "?e2"
227:	.asciiz "?e3"
228:	.asciiz "cpx %02x     "
229:	.asciiz "sbc %02x     "
230:	.asciiz "inc %02x     "
231:	.asciiz "?e7"
232:	.asciiz "inx        "
233:	.asciiz "sbc #%02x    "
234:	.asciiz "nop        "
235:	.asciiz "?eb"
236:	.asciiz "cpx %04x   "
237:	.asciiz "sbc %04x   "
238:	.asciiz "inc %04x   "
239:	.asciiz "?ef"
240:	.asciiz "beq        "
241:	.asciiz "sbc (%02x),y "
242:	.asciiz "?f2"
243:	.asciiz "?f3"
244:	.asciiz "?f4"
245:	.asciiz "sbc %02x,x   "
246:	.asciiz "inc %02x,x   "
247:	.asciiz "?f7"
248:	.asciiz "sed        "
249:	.asciiz "sbc %04x,y "
250:	.asciiz "?fa"
251:	.asciiz "?fb"
252:	.asciiz "?fc"
253:	.asciiz "sbc %04x,x "
254:	.asciiz "inc %04x,x "
255:	.asciiz "?ff"

	.align	4
static_debug_insttbl_ptr:
	.word	static_debug_insttbl,
	.word	1b,2b,3b,4b,5b,6b,7b,8b,9b
	.word	10b,11b,12b,13b,14b,15b,16b,17b,18b,19b
	.word	20b,21b,22b,23b,24b,25b,26b,27b,28b,29b
	.word	30b,31b,32b,33b,34b,35b,36b,37b,38b,39b
	.word	40b,41b,42b,43b,44b,45b,46b,47b,48b,49b
	.word	50b,51b,52b,53b,54b,55b,56b,57b,58b,59b
	.word	60b,61b,62b,63b,64b,65b,66b,67b,68b,69b
	.word	70b,71b,72b,73b,74b,75b,76b,77b,78b,79b
	.word	80b,81b,82b,83b,84b,85b,86b,87b,88b,89b
	.word	90b,91b,92b,93b,94b,95b,96b,97b,98b,99b
	.word	100b,101b,102b,103b,104b,105b,106b,107b,108b,109b
	.word	110b,111b,112b,113b,114b,115b,116b,117b,118b,119b
	.word	120b,121b,122b,123b,124b,125b,126b,127b,128b,129b
	.word	130b,131b,132b,133b,134b,135b,136b,137b,138b,139b
	.word	140b,141b,142b,143b,144b,145b,146b,147b,148b,149b
	.word	150b,151b,152b,153b,154b,155b,156b,157b,158b,159b
	.word	160b,161b,162b,163b,164b,165b,166b,167b,168b,169b
	.word	170b,171b,172b,173b,174b,175b,176b,177b,178b,179b
	.word	180b,181b,182b,183b,184b,185b,186b,187b,188b,189b
	.word	190b,191b,192b,193b,194b,195b,196b,197b,198b,199b
	.word	200b,201b,202b,203b,204b,205b,206b,207b,208b,209b
	.word	210b,211b,212b,213b,214b,215b,216b,217b,218b,219b
	.word	220b,221b,222b,223b,224b,225b,226b,227b,228b,229b
	.word	230b,231b,232b,233b,234b,235b,236b,237b,238b,239b
	.word	240b,241b,242b,243b,244b,245b,246b,247b,248b,249b
	.word	250b,251b,252b,253b,254b,255b
	
	.text
	.align	4
	.set	reorder
	.set	macro
	.set	nobopt
	.set	noat

save_ossync_restore:
	sw	ra, OFFSET_STACK_RA_PRINTF2(sp)
	sw	v0, OFFSET_STACK_V0_PRINTF(sp)
	sw	v1, OFFSET_STACK_V1_PRINTF(sp)
	sw	a1, OFFSET_STACK_A1(sp)
	sw	a2, OFFSET_STACK_A2(sp)
	sw	a3, OFFSET_STACK_A3(sp)
	sw	t0, OFFSET_STACK_T0(sp)
	sw	t1, OFFSET_STACK_T1(sp)
	sw	t2, OFFSET_STACK_T2(sp)
	sw	t3, OFFSET_STACK_T3(sp)
	sw	t4, OFFSET_STACK_T4(sp)
	sw	t5, OFFSET_STACK_T5(sp)
	sw	t6, OFFSET_STACK_T6_PRINTF(sp)
	sw	t7, OFFSET_STACK_T7_PRINTF(sp)
	sw	t8, OFFSET_STACK_T8_PRINTF(sp)
	sw	t9, OFFSET_STACK_T9_PRINTF(sp)
	sw	gp, OFFSET_STACK_GP_PRINTF(sp)
	sw	fp, OFFSET_STACK_FP_PRINTF(sp)
	
	jal	osSyncPrintf
	
	lw	$1, OFFSET_STACK_AT_PRINTF(sp)
	lw	v0, OFFSET_STACK_V0_PRINTF(sp)
	lw	v1, OFFSET_STACK_V1_PRINTF(sp)
	lw	a0, OFFSET_STACK_A0(sp)
	lw	a1, OFFSET_STACK_A1(sp)
	lw	a2, OFFSET_STACK_A2(sp)
	lw	a3, OFFSET_STACK_A3(sp)
	lw	t0, OFFSET_STACK_T0(sp)
	lw	t1, OFFSET_STACK_T1(sp)
	lw	t2, OFFSET_STACK_T2(sp)
	lw	t3, OFFSET_STACK_T3(sp)
	lw	t4, OFFSET_STACK_T4(sp)
	lw	t5, OFFSET_STACK_T5(sp)
	lw	t6, OFFSET_STACK_T6_PRINTF(sp)
	lw	t7, OFFSET_STACK_T7_PRINTF(sp)
	lw	t8, OFFSET_STACK_T8_PRINTF(sp)
	lw	t9, OFFSET_STACK_T9_PRINTF(sp)
	lw	gp, OFFSET_STACK_GP_PRINTF(sp)
	lw	fp, OFFSET_STACK_FP_PRINTF(sp)
	lw	ra, OFFSET_STACK_RA_PRINTF2(sp)

	.set	noreorder
	jr	ra
	lw	ra, OFFSET_STACK_RA_PRINTF(sp)
	.set	reorder
#endif

/* 起動時の load, store func 関数ポインタ */
	.data
	.align	4
nes_initial_functbl:
	.word	store_2000,  store_ppu_ram, store_ignore, store_ppu_ram
	.word	store_2004, store_2005, store_2006, store_2007_chrrom
/* 4000-4026(4027) storefunc */
	.word	store_sound, store_sound, store_sound, store_sound
	.word	store_sound, store_sound, store_sound, store_sound
	.word	store_sound, store_sound, store_sound, store_sound
	.word	store_sound, store_sound, store_sound, store_sound

#define store_4010 store_sound
#define store_4015 store_sound
	.word	store_4010,  store_sound, store_sound, store_sound
	.word	store_4014,  store_4015,  store_4016,  store_4017
	.word	store_invalid, store_invalid, store_invalid, store_invalid
	.word	store_invalid, store_invalid, store_invalid, store_invalid

	.word	storeqd_4020, storeqd_4021, storeqd_4022, storeqd_4023
	.word	storeqd_4024, storeqd_4025, storeqd_4026, store_invalid

/* 0000-, 2000-, ..., e000- storefunc */
	.word	store_internal_wram, store_ppu, store_io, store_invalid
	.word	store_invalid, store_invalid, store_invalid, store_invalid
	.word	store_internal_wram /* a0 > 0xffff の対策 - まず来ないだろうけど u64 alignment もあるし */
/* 0000-, 2000-, ..., e000- loadfunc */
	.word	load_internal_wram, load_ppu, load_io, load_invalid
	.word	load_6000, load_6000, load_6000, load_6000
	.word	load_internal_wram_10000 /* a0 > 0xffff の対策 */
/* 4000-4017 loadfunc */
	.word	load_invalid, load_invalid, load_invalid, load_invalid
	.word	load_invalid, load_invalid, load_invalid, load_invalid
	.word	load_invalid, load_invalid, load_invalid, load_invalid
	.word	load_invalid, load_invalid, load_invalid, load_invalid

	.word	load_invalid, load_invalid, load_invalid, load_invalid
	.word	load_invalid, load_4015,    load_4016,    load_4017

	.data
	.align	4
mmc5_store_functbl_org:
/*  MMC5 のとき 5100-5131 の storefunc
 * 5104: exram mode
 * 5105: vram bank
 * 5106: vram3 chr no
 * 5107: vram3 area code
 */
	.word	store05_5100, store05_5101, store05_5102, store05_5103
	.word	store05_5104, store05_5105, store05_5106, store05_5107
	.word	store_invalid, store_invalid, store_invalid, store_invalid
	.word	store_invalid, store_invalid, store_invalid, store_invalid
/* 5115: br5 */
	.word	store_invalid, store_invalid, store_invalid, store05_5113
	.word	store05_5114, store05_5115, store05_5116, store05_5117
	.word	store_invalid, store_invalid, store_invalid, store_invalid
	.word	store_invalid, store_invalid, store_invalid, store_invalid
/* 5120-27,28-2b: pbr0-7, 4'-7' */
	.word	store05_5120, store05_5120, store05_5120, store05_5120
	.word	store05_5120, store05_5120, store05_5120, store05_5120
	.word	store05_5120, store05_5120, store05_5120, store05_5120
	.word	store_invalid, store_invalid, store_invalid, store_invalid
	
	.word	store05_5130, store_invalid, store_invalid, store_invalid

/* マッパー依存の load/store func table
 * 最初に そのマッパーの初期化のために、特別なコードを呼ぶならそのアドレスを、
 * 汎用の初期化でいいなら 0 を。
 * 次に、0x8000-, 0xa000-, 0xc000-, 0xe000- の順に store func
 * mapper 0..255 まで。
 */
	.data
	.align	4
store_functbl_mapper0:
/*  0(0x00)=NONE           */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*  1(0x01)=MMC1           */.word init01, store01_8000, store01_8000, store01_8000, store01_8000
/*  2(0x02)=PRG1M, fixC000 */.word init02, store02_8000, store02_8000, store02_8000, store02_8000
/*  3(0x03)=CHR128/256     */.word init03, store03_6000, store03_6000, store03_6000, store03_6000
/*  4(0x04)=MMC3           */.word init04, store04_8000, store04_a000, store04_c000, store04_e000
/*  5(0x05)=MMC5           */.word init05, store_invalid, store_invalid, store_invalid, store_invalid
/*  6(0x06)=FFE F4xxx      */.word init06, store06_8000, store_invalid, store_invalid, store_invalid
/*  7(0x07)=Rare           */.word init07, store07_8000, store07_8000, store07_8000, store07_8000
/*  8(0x08)=FFE-F3         */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*  9(0x09)=MMC2           */.word init09, store_invalid, store09_a000, store09_c000, store09_e000
/* 10(0x0a)=MMC4           */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 11(0x0b)=ColorDreams    */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 12(0x0c)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 13(0x0d)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 14(0x0e)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 15(0x0f)=100-in-1       */.word init0f, store0f_8000, store_invalid, store_invalid, store_invalid
/* 16(0x10)=Bandai         */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 17(0x11)=FFE-F8         */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 18(0x12)=Jaleco ss8806  */.word init12, store12_8000, store12_a000, store12_a000, store12_e000
/* 19(0x13)=Namcot 106     */.word init13, store13_8000, store13_a000, store13_c000, store13_e000
/* 20(0x14)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 21(0x15)=VRC4           */.word init15, store19_8000, store19_a000, store19_b000, store19_e000
/* 22(0x16)=VRC2-Shift     */.word init16, store16_8000, store16_a000, store16_b000, store16_b000
/* 23(0x17)=VRC2/4-Odd     */.word init17, store16_8000, store17_a000, store17_b000, store17_e000
/* 24(0x18)=VRC6-A         */.word init18, store18_8000, store18_a000, store18_c000, store18_e000
/* 25(0x19)=VRC2/4 Even    */.word init19, store19_8000, store19_a000, store19_b000, store19_e000
/* 26(0x1a)=VRC6-B         */.word init1a, store18_8000, store18_a000, store18_c000, store18_e000
/* 27(0x1b)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 28(0x1c)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 29(0x1d)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 30(0x1e)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 31(0x1f)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 32(0x20)=Irem G-101     */.word init20, store20_8000, store20_a000, store_invalid, store_invalid
/* 33(0x21)=Taito tc190    */.word init21, store21_8000, store21_a000, store21_c000, store_invalid
/* 34(0x22)=Nina-1         */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 35(0x23)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 36(0x24)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 37(0x25)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 38(0x26)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 39(0x27)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 40(0x28)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 41(0x29)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 42(0x2a)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 43(0x2b)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 44(0x2c)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 45(0x2d)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 46(0x2e)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 47(0x2f)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 48(0x30)=FlintStone     */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 49(0x31)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 50(0x32)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 51(0x33)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 52(0x34)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 53(0x35)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 54(0x36)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 55(0x37)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 56(0x38)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 57(0x39)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 58(0x3a)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 59(0x3b)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 60(0x3c)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 61(0x3d)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 62(0x3e)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 63(0x3f)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 64(0x40)=Tengen Rambo-1 */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 65(0x41)=Irem h-3001    */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 66(0x42)=GNROM          */.word init42, store42_8000, store42_8000, store42_8000, store42_8000
/* 67(0x43)=Sunsoft 3      */.word init43, store44_8000, store44_8000, store43_c000, store44_e000
/* 68(0x44)=Sunsoft 4      */.word init44, store44_8000, store44_8000, store44_c000, store44_e000
/* 69(0x45)=Sunsoft 5      */.word init45, store45_8000, store45_a000, store_invalid, store_invalid
/* 70(0x46)=Kitarou2       */.word init46, store46_8000, store46_8000, store46_8000, store46_8000
/* 71(0x47)=Camerica       */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 72(0x48)=PinballQuest   */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 73(0x49)=VRC3           */.word init49, store_ignore, store49_a000, store49_c000, store02_8000
/* 74(0x4a)=Taiwan MMC3    */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 75(0x4b)=Jale 8805/VRC1 */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 76(0x4c)=Namcot 108/109 */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 77(0x4d)=NapoleonSenki  */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 78(0x4e)=Irem 74hc161   */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 79(0x4f)=AmericanVideo  */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 80(0x50)=Taito X-005    */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 81(0x51)=Taito C075     */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 82(0x52)=Taito X1-17    */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 83(0x53)=PC-Cony Mapper */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 84(0x54)=PC-SMB2J       */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 85(0x55)=VRC7           */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 86(0x56)=Jaleco Moepro  */.word init56, store_invalid, store_invalid, store_invalid, store_invalid
/* 87(0x57)=Hyp_tono       */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 88(0x58)=Namco 118      */.word init58, store58_8000, store_invalid, store_invalid, store_invalid
/* 89(0x59)=Mitokoumon     */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 90(0x5a)=PC-JY-??       */.word init5a, store5a_8000, store5a_a000, store5a_c000, store_invalid
/* 91(0x5b)=HK-SF3         */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 92(0x5c)=Jaleco Moepr88 */.word init5c, store5c_8000, store_invalid, store_invalid, store_invalid
#ifdef KS_RFC_MAPPER_EXTENSION
/* 93(0x5d)=FantasyZone    */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 94(0x5e)=Senjono Okami  */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 95(0x5f)=Namco Dbuster  */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 96(0x60)=Anpanman Hirag */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 97(0x61)=KaiketsuYancha */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 98(0x62)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/* 99(0x63)=VS UniSystem   */.word init63, store_invalid, store_invalid, store_invalid, store_invalid
/*100(0x64)=Hacked BugMMC3 */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*101(0x65)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*102(0x66)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*103(0x67)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*104(0x68)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*105(0x69)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*106(0x6a)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*107(0x6b)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*108(0x6c)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*109(0x6d)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*110(0x6e)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*111(0x6f)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*112(0x70)=PC-Asder       */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*113(0x71)=Sachen/Hacker  */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*114(0x72)=PC-SuperGames  */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*115(0x73)=PC-CartSaint   */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*116(0x74)=AV beauty fght */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*117(0x75)=Sangokushi 4   */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*118(0x76)=Alumagiro J    */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*119(0x77)=TQROM pinbot   */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*120(0x78)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*121(0x79)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*122(0x7a)=MadulaNoTubasa */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*123(0x7b)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*124(0x7c)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*125(0x7d)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*126(0x7e)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*127(0x7f)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*128(0x80)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*129(0x81)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*130(0x82)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*131(0x83)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*132(0x84)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*133(0x85)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*134(0x86)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*135(0x87)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*136(0x88)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*137(0x89)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*138(0x8a)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*139(0x8b)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*140(0x8c)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*141(0x8d)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*142(0x8e)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*143(0x8f)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*144(0x90)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*145(0x91)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*146(0x92)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*147(0x93)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*148(0x94)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*149(0x95)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*150(0x96)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*151(0x97)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*152(0x98)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*153(0x99)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*154(0x9a)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*155(0x9b)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*156(0x9c)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*157(0x9d)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*158(0x9e)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*159(0x9f)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*160(0xa0)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*161(0xa1)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*162(0xa2)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*163(0xa3)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*164(0xa4)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*165(0xa5)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*166(0xa6)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*167(0xa7)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*168(0xa8)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*169(0xa9)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*170(0xaa)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*171(0xab)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*172(0xac)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*173(0xad)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*174(0xae)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*175(0xaf)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*176(0xb0)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*177(0xb1)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*178(0xb2)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*179(0xb3)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*180(0xb4)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*181(0xb5)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*182(0xb6)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*183(0xb7)=AltantisNoNazo */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*184(0xb8)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*185(0xb9)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*186(0xba)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*187(0xbb)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*188(0xbc)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*189(0xbd)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*190(0xbe)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*191(0xbf)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*192(0xc0)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*193(0xc1)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*194(0xc2)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*195(0xc3)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*196(0xc4)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*197(0xc5)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*198(0xc6)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*199(0xc7)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*200(0xc8)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*201(0xc9)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*202(0xca)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*203(0xcb)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*204(0xcc)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*205(0xcd)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*206(0xce)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*207(0xcf)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*208(0xd0)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*209(0xd1)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*210(0xd2)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*211(0xd3)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*212(0xd4)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*213(0xd5)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*214(0xd6)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*215(0xd7)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*216(0xd8)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*217(0xd9)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*218(0xda)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*219(0xdb)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*220(0xdc)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*221(0xdd)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*222(0xde)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*223(0xdf)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*224(0xe0)=?              */.word inite0, storee0_8000, storee0_8000, storee0_8000, storee0_8000
/*225(0xe1)=58in1/64in1    */.word inite1, storee1_8000, storee1_8000, storee1_8000, storee1_8000
/*226(0xe2)=76in1          */.word inite2, storee2_8000, storee2_8000, storee2_8000, storee2_8000
/*227(0xe3)=1200in1        */.word inite3, storee3_8000, storee3_8000, storee3_8000, storee3_8000
/*228(0xe4)=Action52       */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*229(0xe5)=31in1          */.word inite5, storee5_8000, storee5_8000, storee5_8000, storee5_8000
/*230(0xe6)=22in1          */.word inite6, storee6_8000, storee6_8000, storee6_8000, storee6_8000
/*231(0xe7)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*232(0xe8)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*233(0xe9)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*234(0xea)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*235(0xeb)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*236(0xec)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*237(0xed)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*238(0xee)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*239(0xef)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*240(0xf0)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*241(0xf1)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*242(0xf2)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*243(0xf3)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*244(0xf4)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*245(0xf5)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*246(0xf6)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*247(0xf7)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*248(0xf8)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*249(0xf9)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*250(0xfa)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*251(0xfb)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*252(0xfc)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*253(0xfd)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*254(0xfe)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
/*255(0xff)=?              */.word 0, store_invalid, store_invalid, store_invalid, store_invalid
store_functbl_mapper256: /* テーブルのバイト数チェック用シンボル */
#else
/* 253(0xfd)=disksys by myself のみ 追加 */
store_functbl_mapper253:
	.word	initqd, storeqd_8000, storeqd_8000, storeqd_8000, store_invalid
#endif


/* 命令処理テーブル。
 * 16bytes/inst で {func1.w, func2.w, func3.w, nbytes.b, 0.b, cycles.h} とする。
 * 消費 CPU cycle は最高で 8 である。なので 8 -> 0x800 となるように 0x100 倍で持ち、
 * 精密な cycle 計算を目指す。
 * tools/0055.c でテーブル作成 (変更するときは 0055.c を変える!)
 * ... と思ったけど、もう wdm_42 以外 fix しているので、ここを直接変えている。
 */
#define nop_ea next
	.data
	.align	4
inst_jumptbl:
/*brk_00*/.word brk_00,0,0;                     .byte 2,0xff,7,0 /*brk_00*/
/*ora_01*/.word load8_dxi,ora_01,0;             .byte 2,0xff,6,0 /*ora_01*/
/*cop_02*/.word undef_op,0,0;                   .byte 2,0xff,8,0 /*cop_02*/
/*ora_03*/.word undef_op,0,0;                   .byte 2,0xff,4,0 /*ora_03*/
/*tsb_04*/.word undef_op,0,0;                   .byte 2,0xff,5,0 /*tsb_04*/
/*ora_05*/.word load8_zerop,ora_05,0;           .byte 2,0xff,3,0 /*ora_05*/
/*asl_06*/.word load8_zerop,asl_06,0;           .byte 2,0xff,5,0 /*asl_06*/
/*ora_07*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*ora_07*/
/*php_08*/.word php_08,0,0;                     .byte 1,0xff,3,0 /*php_08*/
/*ora_09*/.word load8_imm,ora_09,0;             .byte 2,0xff,2,0 /*ora_09*/
/*asl_0a*/.word asl_0a,0,0;                     .byte 1,0xff,2,0 /*asl_0a*/
/*phd_0b*/.word undef_op,0,0;                   .byte 1,0xff,4,0 /*phd_0b*/
/*tsb_0c*/.word undef_op,0,0;                   .byte 3,0xff,6,0 /*tsb_0c*/
/*ora_0d*/.word load8_abs,ora_0d,0;             .byte 3,0xff,4,0 /*ora_0d*/
/*asl_0e*/.word load8_abs,asl_0e,0;             .byte 3,0xff,6,0 /*asl_0e*/
/*ora_0f*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*ora_0f*/
/*bpl_10*/.word load8_imm,bpl_10,0;             .byte 2,0xff,3,0 /*bpl_10*/
/*ora_11*/.word load8_dyi,ora_11,0;             .byte 2,0xff,5,0 /*ora_11*/
/*ora_12*/.word undef_op,0,0;                   .byte 2,0xff,5,0 /*ora_12*/
/*ora_13*/.word undef_op,0,0;                   .byte 2,0xff,7,0 /*ora_13*/
/*trb_14*/.word undef_op,0,0;                   .byte 2,0xff,5,0 /*trb_14*/
/*ora_15*/.word load8_dx,ora_15,0;              .byte 2,0xff,4,0 /*ora_15*/
/*asl_16*/.word load8_dx,asl_16,0;              .byte 2,0xff,6,0 /*asl_16*/
/*ora_17*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*ora_17*/
/*clc_18*/.word clc_18,0,0;                     .byte 1,0xff,2,0 /*clc_18*/
/*ora_19*/.word load8_absy,ora_19,0;            .byte 3,0xff,4,0 /*ora_19*/
/*inc_1a*/.word undef_op,0,0;                   .byte 1,0xff,2,0 /*inc_1a*/
/*tcs_1b*/.word undef_op,0,0;                   .byte 1,0xff,2,0 /*tcs_1b*/
/*trb_1c*/.word undef_op,0,0;                   .byte 3,0xff,6,0 /*trb_1c*/
/*ora_1d*/.word load8_absx,ora_1d,0;            .byte 3,0xff,4,0 /*ora_1d*/
/*asl_1e*/.word load8_absx,asl_1e,0;            .byte 3,0xff,7,0 /*asl_1e*/
/*ora_1f*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*ora_1f*/
/*jsr_20*/.word load16_imm,jsr_20,0;            .byte 3,0xff,6,0 /*jsr_20*/
/*and_21*/.word load8_dxi,and_21,0;             .byte 2,0xff,6,0 /*and_21*/
/*jsl_22*/.word undef_op,0,0;                   .byte 4,0xff,8,0 /*jsl_22*/
/*and_23*/.word undef_op,0,0;                   .byte 2,0xff,4,0 /*and_23*/
/*bit_24*/.word load8_zerop,bit_24,0;           .byte 2,0xff,3,0 /*bit_24*/
/*and_25*/.word load8_zerop,and_25,0;           .byte 2,0xff,3,0 /*and_25*/
/*rol_26*/.word load8_zerop,rol_26,0;           .byte 2,0xff,5,0 /*rol_26*/
/*and_27*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*and_27*/
/*plp_28*/.word plp_28,0,0;                     .byte 1,0xff,4,0 /*plp_28*/
/*and_29*/.word load8_imm,and_29,0;             .byte 2,0xff,2,0 /*and_29*/
/*rol_2a*/.word rol_2a,0,0;                     .byte 1,0xff,2,0 /*rol_2a*/
/*pld_2b*/.word undef_op,0,0;                   .byte 1,0xff,5,0 /*pld_2b*/
/*bit_2c*/.word load8_abs,bit_2c,0;             .byte 3,0xff,4,0 /*bit_2c*/
/*and_2d*/.word load8_abs,and_2d,0;             .byte 3,0xff,4,0 /*and_2d*/
/*rol_2e*/.word load8_abs,rol_2e,0;             .byte 3,0xff,6,0 /*rol_2e*/
/*and_2f*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*and_2f*/
/*bmi_30*/.word load8_imm,bmi_30,0;             .byte 2,0xff,3,0 /*bmi_30*/
/*and_31*/.word load8_dyi,and_31,0;             .byte 2,0xff,5,0 /*and_31*/
/*and_32*/.word undef_op,0,0;                   .byte 2,0xff,5,0 /*and_32*/
/*and_33*/.word undef_op,0,0;                   .byte 2,0xff,7,0 /*and_33*/
/*bit_34*/.word undef_op,0,0;                   .byte 2,0xff,4,0 /*bit_34*/
/*and_35*/.word load8_dx,and_35,0;              .byte 2,0xff,4,0 /*and_35*/
/*rol_36*/.word load8_dx,rol_36,0;              .byte 2,0xff,6,0 /*rol_36*/
/*and_37*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*and_37*/
/*sec_38*/.word sec_38,0,0;                     .byte 1,0xff,2,0 /*sec_38*/
/*and_39*/.word load8_absy,and_39,0;            .byte 3,0xff,4,0 /*and_39*/
/*dec_3a*/.word undef_op,0,0;                   .byte 1,0xff,2,0 /*dec_3a*/
/*tsc_3b*/.word undef_op,0,0;                   .byte 1,0xff,2,0 /*tsc_3b*/
/*bit_3c*/.word undef_op,0,0;                   .byte 3,0xff,4,0 /*bit_3c*/
/*and_3d*/.word load8_absx,and_3d,0;            .byte 3,0xff,4,0 /*and_3d*/
/*rol_3e*/.word load8_absx,rol_3e,0;            .byte 3,0xff,7,0 /*rol_3e*/
/*and_3f*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*and_3f*/
/*rti_40*/.word rti_40,0,0;                     .byte 1,0xff,6,0 /*rti_40*/
/*eor_41*/.word load8_dxi,eor_41,0;             .byte 2,0xff,6,0 /*eor_41*/
/*wdm_42*/.word wdm_42,0,0;                     .byte 2,0xff,2,0 /*wdm_42*/
/*eor_43*/.word undef_op,0,0;                   .byte 2,0xff,4,0 /*eor_43*/
/*mvp_44*/.word undef_op,0,0;                   .byte 3,0xff,7,0 /*mvp_44*/
/*eor_45*/.word load8_zerop,eor_45,0;           .byte 2,0xff,3,0 /*eor_45*/
/*lsr_46*/.word load8_zerop,lsr_46,0;           .byte 2,0xff,5,0 /*lsr_46*/
/*eor_47*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*eor_47*/
/*pha_48*/.word pha_48,0,0;                     .byte 1,0xff,3,0 /*pha_48*/
/*eor_49*/.word load8_imm,eor_49,0;             .byte 2,0xff,2,0 /*eor_49*/
/*lsr_4a*/.word lsr_4a,0,0;                     .byte 1,0xff,2,0 /*lsr_4a*/
/*phk_4b*/.word undef_op,0,0;                   .byte 1,0xff,3,0 /*phk_4b*/
/*jmp_4c*/.word load16_imm,jmp_4c,0;            .byte 3,0xff,3,0 /*jmp_4c*/
/*eor_4d*/.word load8_abs,eor_4d,0;             .byte 3,0xff,4,0 /*eor_4d*/
/*lsr_4e*/.word load8_abs,lsr_4e,0;             .byte 3,0xff,6,0 /*lsr_4e*/
/*eor_4f*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*eor_4f*/
/*bvc_50*/.word load8_imm,bvc_50,0;             .byte 2,0xff,3,0 /*bvc_50*/
/*eor_51*/.word load8_dyi,eor_51,0;             .byte 2,0xff,5,0 /*eor_51*/
/*eor_52*/.word undef_op,0,0;                   .byte 2,0xff,5,0 /*eor_52*/
/*eor_53*/.word undef_op,0,0;                   .byte 2,0xff,7,0 /*eor_53*/
/*mvn_54*/.word undef_op,0,0;                   .byte 3,0xff,7,0 /*mvn_54*/
/*eor_55*/.word load8_dx,eor_55,0;              .byte 2,0xff,4,0 /*eor_55*/
/*lsr_56*/.word load8_dx,lsr_56,0;              .byte 2,0xff,6,0 /*lsr_56*/
/*eor_57*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*eor_57*/
/*cli_58*/.word cli_58,0,0;                     .byte 1,0xff,2,0 /*cli_58*/
/*eor_59*/.word load8_absy,eor_59,0;            .byte 3,0xff,4,0 /*eor_59*/
/*phy_5a*/.word undef_op,0,0;                   .byte 1,0xff,3,0 /*phy_5a*/
/*tcd_5b*/.word undef_op,0,0;                   .byte 1,0xff,2,0 /*tcd_5b*/
/*jmp_5c*/.word undef_op,0,0;                   .byte 4,0xff,4,0 /*jmp_5c*/
/*eor_5d*/.word load8_absx,eor_5d,0;            .byte 3,0xff,4,0 /*eor_5d*/
/*lsr_5e*/.word load8_absx,lsr_5e,0;            .byte 3,0xff,7,0 /*lsr_5e*/
/*eor_5f*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*eor_5f*/
/*rts_60*/.word rts_60,0,0;                     .byte 1,0xff,6,0 /*rts_60*/
/*adc_61*/.word load8_dxi,adc_61,0;             .byte 2,0xff,6,0 /*adc_61*/
/*per_62*/.word undef_op,0,0;                   .byte 3,0xff,6,0 /*per_62*/
/*adc_63*/.word undef_op,0,0;                   .byte 2,0xff,4,0 /*adc_63*/
/*stz_64*/.word undef_op,0,0;                   .byte 2,0xff,3,0 /*stz_64*/
/*adc_65*/.word load8_zerop,adc_65,0;           .byte 2,0xff,3,0 /*adc_65*/
/*ror_66*/.word load8_zerop,ror_66,0;           .byte 2,0xff,5,0 /*ror_66*/
/*adc_67*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*adc_67*/
/*pla_68*/.word pla_68,0,0;                     .byte 1,0xff,4,0 /*pla_68*/
/*adc_69*/.word load8_imm,adc_69,0;             .byte 2,0xff,2,0 /*adc_69*/
/*ror_6a*/.word ror_6a,0,0;                     .byte 1,0xff,2,0 /*ror_6a*/
/*rtl_6b*/.word undef_op,0,0;                   .byte 1,0xff,6,0 /*rtl_6b*/
/*jmp_6c*/.word load16_imm,jmp_6c,0;            .byte 3,0xff,5,0 /*jmp_6c*/
/*adc_6d*/.word load8_abs,adc_6d,0;             .byte 3,0xff,4,0 /*adc_6d*/
/*ror_6e*/.word load8_abs,ror_6e,0;             .byte 3,0xff,6,0 /*ror_6e*/
/*adc_6f*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*adc_6f*/
/*bvs_70*/.word load8_imm,bvs_70,0;             .byte 2,0xff,3,0 /*bvs_70*/
/*adc_71*/.word load8_dyi,adc_71,0;             .byte 2,0xff,5,0 /*adc_71*/
/*adc_72*/.word undef_op,0,0;                   .byte 2,0xff,5,0 /*adc_72*/
/*adc_73*/.word undef_op,0,0;                   .byte 2,0xff,7,0 /*adc_73*/
/*stz_74*/.word undef_op,0,0;                   .byte 2,0xff,4,0 /*stz_74*/
/*adc_75*/.word load8_dx,adc_75,0;              .byte 2,0xff,4,0 /*adc_75*/
/*ror_76*/.word load8_dx,ror_76,0;              .byte 2,0xff,6,0 /*ror_76*/
/*adc_77*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*adc_77*/
/*sei_78*/.word sei_78,0,0;                     .byte 1,0xff,2,0 /*sei_78*/
/*adc_79*/.word load8_absy,adc_79,0;            .byte 3,0xff,4,0 /*adc_79*/
/*ply_7a*/.word undef_op,0,0;                   .byte 1,0xff,4,0 /*ply_7a*/
/*tdc_7b*/.word undef_op,0,0;                   .byte 1,0xff,2,0 /*tdc_7b*/
/*jmp_7c*/.word undef_op,0,0;                   .byte 3,0xff,6,0 /*jmp_7c*/
/*adc_7d*/.word load8_absx,adc_7d,0;            .byte 3,0xff,4,0 /*adc_7d*/
/*ror_7e*/.word load8_absx,ror_7e,0;            .byte 3,0xff,7,0 /*ror_7e*/
/*adc_7f*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*adc_7f*/
/*bra_80*/.word undef_op,0,0;                   .byte 2,0xff,2,0 /*bra_80*/
/*sta_81*/.word load8_dx,sta_81,0;              .byte 2,0xff,6,0 /*sta_81*/
/*brl_82*/.word undef_op,0,0;                   .byte 3,0xff,4,0 /*brl_82*/
/*sta_83*/.word undef_op,0,0;                   .byte 2,0xff,4,0 /*sta_83*/
/*sty_84*/.word load8_imm,sty_84,0;             .byte 2,0xff,3,0 /*sty_84*/
/*sta_85*/.word load8_imm,sta_85,0;             .byte 2,0xff,3,0 /*sta_85*/
/*stx_86*/.word load8_imm,stx_86,0;             .byte 2,0xff,3,0 /*stx_86*/
/*sta_87*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*sta_87*/
/*dey_88*/.word dey_88,0,0;                     .byte 1,0xff,2,0 /*dey_88*/
/*bit_89*/.word undef_op,0,0;                   .byte 2,0xff,2,0 /*bit_89*/
/*txa_8a*/.word txa_8a,0,0;                     .byte 1,0xff,2,0 /*txa_8a*/
/*phb_8b*/.word undef_op,0,0;                   .byte 1,0xff,3,0 /*phb_8b*/
/*sty_8c*/.word load16_imm,sty_8c,0;            .byte 3,0xff,4,0 /*sty_8c*/
/*sta_8d*/.word load16_imm,sta_8d,0;            .byte 3,0xff,4,0 /*sta_8d*/
/*stx_8e*/.word load16_imm,stx_8e,0;            .byte 3,0xff,4,0 /*stx_8e*/
/*sta_8f*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*sta_8f*/
/*bcc_90*/.word load8_imm,bcc_90,0;             .byte 2,0xff,3,0 /*bcc_90*/
/*sta_91*/.word load8_zerop,sta_91,0;           .byte 2,0xff,6,0 /*sta_91*/
/*sta_92*/.word undef_op,0,0;                   .byte 2,0xff,5,0 /*sta_92*/
/*sta_93*/.word undef_op,0,0;                   .byte 2,0xff,7,0 /*sta_93*/
/*sty_94*/.word load8_imm,sty_94,0;             .byte 2,0xff,4,0 /*sty_94*/
/*sta_95*/.word load8_imm,sta_95,0;             .byte 2,0xff,4,0 /*sta_95*/
/*stx_96*/.word load8_imm,stx_96,0;             .byte 2,0xff,4,0 /*stx_96*/
/*sta_97*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*sta_97*/
/*tya_98*/.word tya_98,0,0;                     .byte 1,0xff,2,0 /*tya_98*/
/*sta_99*/.word load16_imm,sta_99,0;            .byte 3,0xff,5,0 /*sta_99*/
/*txs_9a*/.word txs_9a,0,0;                     .byte 1,0xff,2,0 /*txs_9a*/
/*txy_9b*/.word undef_op,0,0;                   .byte 1,0xff,2,0 /*txy_9b*/
/*stz_9c*/.word undef_op,0,0;                   .byte 3,0xff,4,0 /*stz_9c*/
/*sta_9d*/.word load16_imm,sta_9d,0;            .byte 3,0xff,5,0 /*sta_9d*/
/*stz_9e*/.word undef_op,0,0;                   .byte 3,0xff,5,0 /*stz_9e*/
/*sta_9f*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*sta_9f*/
/*ldy_a0*/.word load8_imm,ldy_a0,0;             .byte 2,0xff,2,0 /*ldy_a0*/
/*lda_a1*/.word load8_dxi,lda_a1,0;             .byte 2,0xff,6,0 /*lda_a1*/
/*ldx_a2*/.word load8_imm,ldx_a2,0;             .byte 2,0xff,2,0 /*ldx_a2*/
/*lda_a3*/.word undef_op,0,0;                   .byte 2,0xff,4,0 /*lda_a3*/
/*ldy_a4*/.word load8_zerop,ldy_a4,0;           .byte 2,0xff,3,0 /*ldy_a4*/
/*lda_a5*/.word load8_zerop,lda_a5,0;           .byte 2,0xff,3,0 /*lda_a5*/
/*ldx_a6*/.word load8_zerop,ldx_a6,0;           .byte 2,0xff,3,0 /*ldx_a6*/
/*lda_a7*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*lda_a7*/
/*tay_a8*/.word tay_a8,0,0;                     .byte 1,0xff,2,0 /*tay_a8*/
/*lda_a9*/.word load8_imm,lda_a9,0;             .byte 2,0xff,2,0 /*lda_a9*/
/*tax_aa*/.word tax_aa,0,0;                     .byte 1,0xff,2,0 /*tax_aa*/
/*plb_ab*/.word undef_op,0,0;                   .byte 1,0xff,4,0 /*plb_ab*/
/*ldy_ac*/.word load8_abs,ldy_ac,0;             .byte 3,0xff,4,0 /*ldy_ac*/
/*lda_ad*/.word load8_abs,lda_ad,0;             .byte 3,0xff,4,0 /*lda_ad*/
/*ldx_ae*/.word load8_abs,ldx_ae,0;             .byte 3,0xff,4,0 /*ldx_ae*/
/*lda_af*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*lda_af*/
/*bcs_b0*/.word load8_imm,bcs_b0,0;             .byte 2,0xff,3,0 /*bcs_b0*/
/*lda_b1*/.word load8_dyi,lda_b1,0;             .byte 2,0xff,5,0 /*lda_b1*/
/*lda_b2*/.word undef_op,0,0;                   .byte 2,0xff,5,0 /*lda_b2*/
/*lda_b3*/.word undef_op,0,0;                   .byte 2,0xff,7,0 /*lda_b3*/
/*ldy_b4*/.word load8_dx,ldy_b4,0;              .byte 2,0xff,4,0 /*ldy_b4*/
/*lda_b5*/.word load8_dx,lda_b5,0;              .byte 2,0xff,4,0 /*lda_b5*/
/*ldx_b6*/.word load8_imm,ldx_b6,0;             .byte 2,0xff,4,0 /*ldx_b6*/
/*lda_b7*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*lda_b7*/
/*clv_b8*/.word clv_b8,0,0;                     .byte 1,0xff,2,0 /*clv_b8*/
/*lda_b9*/.word load8_absy,lda_b9,0;            .byte 3,0xff,4,0 /*lda_b9*/
/*tsx_ba*/.word tsx_ba,0,0;                     .byte 1,0xff,2,0 /*tsx_ba*/
/*tyx_bb*/.word undef_op,0,0;                   .byte 1,0xff,2,0 /*tyx_bb*/
/*ldy_bc*/.word load8_absx,ldy_bc,0;            .byte 3,0xff,4,0 /*ldy_bc*/
/*lda_bd*/.word load8_absx,lda_bd,0;            .byte 3,0xff,4,0 /*lda_bd*/
/*ldx_be*/.word load8_absy,ldx_be,0;            .byte 3,0xff,4,0 /*ldx_be*/
/*lda_bf*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*lda_bf*/
/*cpy_c0*/.word load8_imm,cpy_c0,0;             .byte 2,0xff,2,0 /*cpy_c0*/
/*cmp_c1*/.word load8_dxi,cmp_c1,0;             .byte 2,0xff,6,0 /*cmp_c1*/
/*rep_c2*/.word undef_op,0,0;                   .byte 2,0xff,3,0 /*rep_c2*/
/*cmp_c3*/.word undef_op,0,0;                   .byte 2,0xff,4,0 /*cmp_c3*/
/*cpy_c4*/.word load8_zerop,cpy_c4,0;           .byte 2,0xff,3,0 /*cpy_c4*/
/*cmp_c5*/.word load8_zerop,cmp_c5,0;           .byte 2,0xff,3,0 /*cmp_c5*/
/*dec_c6*/.word load8_zerop,dec_c6,0;           .byte 2,0xff,5,0 /*dec_c6*/
/*cmp_c7*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*cmp_c7*/
/*iny_c8*/.word iny_c8,0,0;                     .byte 1,0xff,2,0 /*iny_c8*/
/*cmp_c9*/.word load8_imm,cmp_c9,0;             .byte 2,0xff,2,0 /*cmp_c9*/
/*dex_ca*/.word dex_ca,0,0;                     .byte 1,0xff,2,0 /*dex_ca*/
/*wai_cb*/.word undef_op,0,0;                   .byte 1,0xff,3,0 /*wai_cb*/
/*cpy_cc*/.word load8_abs,cpy_cc,0;             .byte 3,0xff,4,0 /*cpy_cc*/
/*cmp_cd*/.word load8_abs,cmp_cd,0;             .byte 3,0xff,4,0 /*cmp_cd*/
/*dec_ce*/.word load8_abs,dec_ce,0;             .byte 3,0xff,6,0 /*dec_ce*/
/*cmp_cf*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*cmp_cf*/
/*bne_d0*/.word load8_imm,bne_d0,0;             .byte 2,0xff,3,0 /*bne_d0*/
/*cmp_d1*/.word load8_dyi,cmp_d1,0;             .byte 2,0xff,5,0 /*cmp_d1*/
/*cmp_d2*/.word undef_op,0,0;                   .byte 2,0xff,5,0 /*cmp_d2*/
/*cmp_d3*/.word undef_op,0,0;                   .byte 2,0xff,7,0 /*cmp_d3*/
/*pei_d4*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*pei_d4*/
/*cmp_d5*/.word load8_dx,cmp_d5,0;              .byte 2,0xff,4,0 /*cmp_d5*/
/*dec_d6*/.word load8_dx,dec_d6,0;              .byte 2,0xff,6,0 /*dec_d6*/
/*cmp_d7*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*cmp_d7*/
/*cld_d8*/.word cld_d8,0,0;                     .byte 1,0xff,2,0 /*cld_d8*/
/*cmp_d9*/.word load8_absy,cmp_d9,0;            .byte 3,0xff,4,0 /*cmp_d9*/
/*phx_da*/.word undef_op,0,0;                   .byte 1,0xff,3,0 /*phx_da*/
/*stp_db*/.word undef_op,0,0;                   .byte 1,0xff,3,0 /*stp_db*/
/*jml_dc*/.word undef_op,0,0;                   .byte 3,0xff,6,0 /*jml_dc*/
/*cmp_dd*/.word load8_absx,cmp_dd,0;            .byte 3,0xff,4,0 /*cmp_dd*/
/*dec_de*/.word load8_absx,dec_de,0;            .byte 3,0xff,7,0 /*dec_de*/
/*cmp_df*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*cmp_df*/
/*cpx_e0*/.word load8_imm,cpx_e0,0;             .byte 2,0xff,2,0 /*cpx_e0*/
/*sbc_e1*/.word load8_dxi,sbc_e1,0;             .byte 2,0xff,6,0 /*sbc_e1*/
/*sep_e2*/.word undef_op,0,0;                   .byte 2,0xff,3,0 /*sep_e2*/
/*sbc_e3*/.word undef_op,0,0;                   .byte 2,0xff,4,0 /*sbc_e3*/
/*cpx_e4*/.word load8_zerop,cpx_e4,0;           .byte 2,0xff,3,0 /*cpx_e4*/
/*sbc_e5*/.word load8_zerop,sbc_e5,0;           .byte 2,0xff,3,0 /*sbc_e5*/
/*inc_e6*/.word load8_zerop,inc_e6,0;           .byte 2,0xff,5,0 /*inc_e6*/
/*sbc_e7*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*sbc_e7*/
/*inx_e8*/.word inx_e8,0,0;                     .byte 1,0xff,2,0 /*inx_e8*/
/*sbc_e9*/.word load8_imm,sbc_e9,0;             .byte 2,0xff,2,0 /*sbc_e9*/
/*nop_ea*/.word nop_ea,0,0;                     .byte 1,0xff,2,0 /*nop_ea*/
/*xba_eb*/.word undef_op,0,0;                   .byte 1,0xff,3,0 /*xba_eb*/
/*cpx_ec*/.word load8_abs,cpx_ec,0;             .byte 3,0xff,4,0 /*cpx_ec*/
/*sbc_ed*/.word load8_abs,sbc_ed,0;             .byte 3,0xff,4,0 /*sbc_ed*/
/*inc_ee*/.word load8_abs,inc_ee,0;             .byte 3,0xff,6,0 /*inc_ee*/
/*sbc_ef*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*sbc_ef*/
/*beq_f0*/.word load8_imm,beq_f0,0;             .byte 2,0xff,3,0 /*beq_f0*/
/*sbc_f1*/.word load8_dyi,sbc_f1,0;             .byte 2,0xff,5,0 /*sbc_f1*/
/*sbc_f2*/.word undef_op,0,0;                   .byte 2,0xff,5,0 /*sbc_f2*/
/*sbc_f3*/.word undef_op,0,0;                   .byte 2,0xff,7,0 /*sbc_f3*/
/*pea_f4*/.word undef_op,0,0;                   .byte 3,0xff,5,0 /*pea_f4*/
/*sbc_f5*/.word load8_dx,sbc_f5,0;              .byte 2,0xff,4,0 /*sbc_f5*/
/*inc_f6*/.word load8_dx,inc_f6,0;              .byte 2,0xff,6,0 /*inc_f6*/
/*sbc_f7*/.word undef_op,0,0;                   .byte 2,0xff,6,0 /*sbc_f7*/
/*sed_f8*/.word sed_f8,0,0;                     .byte 1,0xff,2,0 /*sed_f8*/
/*sbc_f9*/.word load8_absy,sbc_f9,0;            .byte 3,0xff,4,0 /*sbc_f9*/
/*plx_fa*/.word undef_op,0,0;                   .byte 1,0xff,4,0 /*plx_fa*/
/*xce_fb*/.word undef_op,0,0;                   .byte 1,0xff,2,0 /*xce_fb*/
/*jsr_fc*/.word undef_op,0,0;                   .byte 3,0xff,6,0 /*jsr_fc*/
/*sbc_fd*/.word load8_absx,sbc_fd,0;            .byte 3,0xff,4,0 /*sbc_fd*/
/*inc_fe*/.word load8_absx,inc_fe,0;            .byte 3,0xff,7,0 /*inc_fe*/
/*sbc_ff*/.word undef_op,0,0;                   .byte 4,0xff,5,0 /*sbc_ff*/
	
	.text
	.align	4
	.set	reorder
	.set	macro
	.set	nobopt
	.set	noat

/* extern int ks_reset_rfcstate_asm(rfcstate_object *p);
 * 何回呼んでも良い。
 * ただし、wram を 0xff クリアするので HotStart はできない。
 * 話はそれるが 0 で初期化すると HotStart と勘違いするダメソフトが
 * あったような。d_bowl.nes
 */
	.globl	ks_reset_rfcstate_asm
	.ent	ks_reset_rfcstate_asm
	.frame	sp, EMU_RFC_ASM_STACKSIZE, ra
ks_reset_rfcstate_asm:
	addiu	sp, sp, -EMU_RFC_ASM_STACKSIZE
	sw	s0, OFFSET_STACK_S0(sp)
	sw	s1, OFFSET_STACK_S1(sp)
	sw	s2, OFFSET_STACK_S2(sp)
	sw	s3, OFFSET_STACK_S3(sp)
	sw	s4, OFFSET_STACK_S4(sp)
	sw	s5, OFFSET_STACK_S5(sp)
	sw	s6, OFFSET_STACK_S6(sp)
	sw	s7, OFFSET_STACK_S7(sp)
	sw	gp, OFFSET_STACK_GP(sp)
	sw	fp, OFFSET_STACK_FP(sp)
	sw	ra, OFFSET_STACK_RA(sp)

	move	WORK, a0

/*PRINTF("ks_reset_rfcstate_asm called\n");*/

	sw	zero, OFFSET_EMUFRAME(WORK)
	sb	zero, OFFSET_PC_HIST_OFFSET(WORK)
/* line (H-blank) 開始から H-blank 終了までには 約 28 cpu cycle
 *                終了から line end までには    約 85 cpu cycle
 * これを 28+85.75 とすると
 * 28    * 0x100 = 0x1c00
 * 85.75 * 0x100 = 0x55c0
 * H-blank の終了をチェックするために line 開始時に LINECYCLE の d0=1 に
 * しておく。
 */

#define LINECYCLE_CONST0 (-0x1c00 | 0x01)
#define LINECYCLE_CONST1 (-0x55c0)

	li	t0, LINECYCLE_CONST0
	sw	t0, OFFSET_THREAD_LINECYCLE(WORK)
	sh	zero, OFFSET_LINECNT(WORK)

/* 最初の実行アドレスは emu_line0 */
	la	t0, emu_line0
	sw	t0, OFFSET_THREAD_PC(WORK)
	
/* clear wram, trainer, oam = 0xff
 * t0 = dst for wram
 * t1 = dst for trainer
 * t2 = dst for wram end
 * t3 = const
 * t4 = dst for oam
 * 0x80 回 repeat
 */
	move	t0, WORK
	move	t1, WORK
	move	t4, WORK
	addiu	t2, t0, 0x800
	li	t3, -1
1:
#if 0 /* wram＆bbramのクリアを上の方でやる */
        sw	t3, OFFSET_WRAM(t0)
	sw	t3, OFFSET_WRAM + 4(t0)
	sw	t3, OFFSET_WRAM + 8(t0)
	sw	t3, OFFSET_WRAM + 0xc(t0)
	sw	t3, OFFSET_BBRAM + 0x1000(t1)
#endif
	sh	t3, OFFSET_OAM(t4)
	addiu	t0, t0, 0x10
	addiu	t1, t1, 0x4
	addiu	t4, t4, 0x2
	bne	t0, t2, 1b

#if 0 /* wram＆bbramのクリアを上の方でやる */
/* 0xd3 番地は < 0x80 にしておく
 * sun/metafght.nes がそこをクリアしないで MMC1 レジスタに書き込んでいるので
 * d7=1 ではレジスタリセットになってしまう
 */
	li	t0, 0x7f
	sb	t0, OFFSET_WRAM + 0xd3(WORK)
#endif
	
/* clear vram, draw vram */
	move	t0, WORK
	addiu	t1, t0, SIZEOF_DRAW_VRAM
1:
	sd	zero, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + 0(t0)
	sd	zero, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + 8(t0)
	addiu	t0, t0, 0x10
	bne	t0, t1, 1b

/* VRAM mirror
 * header[6] の d0 が 0 なら 縦持ち
 *                    1 なら 横持ち
 * で初期化しておく
 */
	lw	t0, OFFSET_HEADERP(WORK)
	lbu	t1, 6(t0)
	andi	t1, t1, 0x01
	li	t2, 0x400    /* 横持ち */
	bne	t1, zero, 1f
	li	t2, 0x800    /* 縦持ち */
1:
	sh	t2, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	sh	zero, OFFSET_VRAM_MIRROR_MASK_OR(WORK)

#if 0
/* chr touch flag は all dirty とする。*/
	move	t0, WORK
	addiu	t1, t0, SIZEOF_CHR_TOUCH_FLAG
1:
	sw	zero, OFFSET_GFXBUF + OFFSET_GFXBUF_CHR_TOUCH_FLAG(t0)
	addiu	t0, t0, 4
	bne	t0, t1, 1b
#endif

/* serial 初期化
 */
	sd	zero, OFFSET_SERIAL4016D1(WORK)
	sd	zero, OFFSET_SERIAL4017D5(WORK)
	sd	zero, OFFSET_SERIAL4017D3(WORK)
	sd	zero, OFFSET_SERIAL4017D1(WORK)
	sd	zero, OFFSET_WORK_4016D1(WORK)
	sd	zero, OFFSET_WORK_4017D5(WORK)
	sd	zero, OFFSET_WORK_4017D3(WORK)
	sd	zero, OFFSET_WORK_4017D1(WORK)
	sb	zero, OFFSET_WORK_4016H(WORK)
/* V ext mode など 割り込み禁止 */
	li	t0, 0x40
	sb	t0, OFFSET_WORK_4017H(WORK)
	sb	zero, OFFSET_IRQ_CAUSE(WORK)
	sb	zero, OFFSET_WORK_4015H_OUTPUT(WORK)

	li	t0, 0xffff
	sh	t0, OFFSET_WORK_4010H_OUTPUT(WORK)
	
/* BBRAM も 0xff でクリア。
 * - chrram と共に、 hot start には未対応。
 */
	move	t0, WORK
	li	t1, OFFSET_BBRAM
	li	t2, OFFSET_BBRAM + 0x2000
	addu	t1, t1, t0
	addu	t2, t2, t0
	li	t3, -1
1:
	sw	t3, 0(t1)
	sw	t4, 4(t1)
	addiu	t1, t1, 8
	bne	t1, t2, 1b
	
/* まず、normal NES として 各種初期化。
 * store_ppu, store_io, storefunc, loadfunc, load_io の順番。
 * page table
 * ppu2000h output
 * ppu2000h
 * qd4030h
 */
	la	t0, nes_initial_functbl
	addiu	t1, t0, OFFSET_LOAD_IO_FUNCTBL + 4 * 0x18 - OFFSET_STORE_PPU_FUNCTBL
	move	t2, WORK
1:
	ld	t3, 0(t0)
	sd	t3, OFFSET_STORE_PPU_FUNCTBL(t2)

#if 0
/* debug */
	lw	a1, 0(t0)
	lw	a2, 4(t0)
	addiu	a3, t2, OFFSET_STORE_PPU_FUNCTBL
	PRINTF("%08x %08x at %08x\n");
#endif
	
	addiu	t0, t0, 8
	addiu	t2, t2, 8
	bne	t0, t1, 1b

/* chrbank 設定関数など */
	la	t0, chrbank_func_normal
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	sb	zero, OFFSET_DRAW_BG_CHRBANK_COUNT(WORK)
	sb	zero, OFFSET_DRAW_OBJ_CHRBANK_COUNT(WORK)

/* page table
 * 0000-5fff はダミー。WORK でも入れとけ。
 * 6000-7fff は bbram - 0x6000
 * 8000-9fff は prgrom - 0x8000 (rombank 0)
 * a000-bfff は prgrom - 0x8000 (rombank 1)
 * c000-dfff は prgrom + prgsize - 0x4000 - 0xc000 (rombank last-1)
 * e000-ffff は prgrom + prgsize - 0x4000 - 0xc000 (rombank last)
 * pbank_mask も設定。(0x2000 bytes 単位)
 */
	sw	WORK, OFFSET_PAGE_TABLE + 0x00(WORK)
	sw	WORK, OFFSET_PAGE_TABLE + 0x04(WORK)
	sw	WORK, OFFSET_PAGE_TABLE + 0x08(WORK)

	addiu	t1, WORK, OFFSET_BBRAM - 0x6000
	sw	t1, OFFSET_PAGE_TABLE + 0x0c(WORK)
	lw	t2, OFFSET_HEADERP(WORK)
	lbu	t3, 6(t2)

	andi	t4, t3, 0x04 | 0x02
	beq	t4, zero, 1f
/* trainer or BBRAM */
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)
	
	andi	t3, t3, 0x04
	beq	t3, zero, 1f
/* trainer */
/* headerp の後に prgrom, chrrom がくっつくのは やめた。
 * その 1/2
 */
	la	t2, ks_nes_nesromstartp
	lw	t2, 0(t2)

	addiu	t3, t2, 0x200

	move	t5, WORK
2:
	lw	t4, 0x10(t2)
	sw	t4, OFFSET_BBRAM + 0x1000(t5)
	addiu	t2, t2, 4
	addiu	t5, t5, 4
	bne	t2, t3, 2b

1:
#if 0
/* headerp の後に prgrom, chrrom がくっつくのは やめた。
 * その 2/2
 */
	addiu	t2, t2, 0x10
	sw	t2, OFFSET_PRGROM(WORK)
#endif
/* prgrom は extern で。
 * 先頭は NES^Z やし。
 */
	la	t2, ks_nes_nesromstartp
	lw	t2, 0(t2)
	addiu	t2, t2, 0x10
/* trainer 対応 */
	lw	t1, OFFSET_HEADERP(WORK)
	lbu	t3, 6(t1)
	andi	t3, t3, 0x04
	beq	t3, zero, 4f
	addiu	t2, t2, 0x200
4:
	sw	t2, OFFSET_PRGROM(WORK)

	lw	t3, OFFSET_PRGSIZE(WORK)
	addu	t4, t2, t3
	addiu	t2, t2, -0x8000
	sw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x14(WORK)
	addiu	t2, t2, -0x8000  /* これで t2 = prgrom - 0x4000 - 0xc000 */
	addu	t2, t2, t3
	sw	t2, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x1c(WORK)
/* pbank_mask = (prgsize / 0x2000) - 1 */
	srl	t1, t3, 13
	addiu	t1, t1, -1
	sb	t1, OFFSET_PBANK_MASK(WORK)

/* chr offset mask は byte 単位。*/
	lw	t2, OFFSET_CHRSIZE(WORK)
	sw	t4, OFFSET_CHRROM(WORK)
	sb	zero, OFFSET_MMC1_IGNORE_CHRBANK(WORK)
	bne	t2, zero, 1f
/* chrram なら storefunc も変更。*/
	li	t2, 0xff
	sb	t2, OFFSET_MMC1_IGNORE_CHRBANK(WORK)
	la	t2, store_2007_chrram
	sw	t2, OFFSET_STORE_PPU_FUNCTBL + 7 * 4(WORK)
	li	t2, 0x2000
1:
	addiu	t2, t2, -1
	sw	t2, OFFSET_CHR_OFFSET_MASK(WORK)

/* chr data の conversion
 * chrram なら 無視。
 */
	lw	a0, OFFSET_CHRROM(WORK)
	lw	a2, OFFSET_UCODE_WORKP(WORK)
	lw	a3, OFFSET_CHRSIZE(WORK)
	
	addiu	a2, a2, OFFSET_UCODEWK_DRAW_CHR
	bne	a3, zero, 1f
/* chrram なら 0 クリア
 * - hot start には未対応
 */
	addiu	a3, a2, 0x2000
2:
	sd	zero, 0(a2)
	sd	zero, 8(a2)
	addiu	a2, a2, 0x10
	bne	a2, a3, 2b
	
	j	10f
1:
	li	a1, 0
/* 引数 (keep される)
 * a0: chrrom top
 * a1: chrrom offset
 * a2: dst top
 * (a3: chrrom offset end)
 * 1 char ずつ  convert される。
 */
1:
	jal	convert_chrdata
	addiu	a1, a1, 0x10
	bne	a1, a3, 1b

10:
/* マイクロコードで chrram/chrrom 共通処理をしているので
 * ucode work chrrom 先頭 の 0x2000 bytes は draw chrrom にも copy.
 * goemon1j.nes 対策で 0x4000 bytes にしてみる。
 */
	addiu	a0, WORK, OFFSET_GFXBUF
	lw	a1, OFFSET_UCODE_WORKP(WORK)
	addiu	a2, a0, 0x4000
1:
	ld	t0, OFFSET_UCODEWK_DRAW_CHR + 0(a1)
	ld	t1, OFFSET_UCODEWK_DRAW_CHR + 8(a1)
	sd	t0, OFFSET_GFXBUF_DRAW_CHR + 0(a0)
	sd	t1, OFFSET_GFXBUF_DRAW_CHR + 8(a0)
	addiu	a0, a0, 0x10
	addiu	a1, a1, 0x10
	bne	a0, a2, 1b
	
/* ppu2000h output */
	li	t0, 0x20202020
	sw	t0, OFFSET_PPU2000H_OUTPUT + 0(WORK)
	sw	t0, OFFSET_PPU2000H_OUTPUT + 4(WORK)
/* draw scch, sccv */
	sb	zero, OFFSET_DRAW_SCCH(WORK)
	sh	zero, OFFSET_DRAW_SCCV_NEW(WORK)
/* ppu2000h */
	sd	zero, OFFSET_PPU2000H(WORK)
/* qd output */
/*	li	t0, 0x000047ff */
/* debug
 * disksys
 * qd not inserted
 */
	li	t0, 0x0000fa80
	sw	t0, OFFSET_QD4030H(WORK)
	li	t0, 0x27
	sb	t0, OFFSET_QD4020H + 5(WORK)
	sb	t0, OFFSET_QD4025H_BAK(WORK)
/* QD dirty flag を消しておく。
 */
	sb	t0, OFFSET_QD_DIRTY(WORK)

/* strikef */
	li	t1, 0xff
	sb	t1, OFFSET_STRIKEF_LINE(WORK)

/* inst_jumptbl -> jumptbl へ copy */
	la	t0, inst_jumptbl
	move	t1, WORK
	addiu	t2, t0, 0x100 * 0x10
1:
	ld	t3, 0(t0)
	ld	t4, 8(t0)
	sd	t3, OFFSET_JUMPTBL + 0(t1)
	sd	t4, OFFSET_JUMPTBL + 8(t1)
	addiu	t0, t0, 0x10
	addiu	t1, t1, 0x10
	bne	t0, t2, 1b

/* A=X=Y=0
 * SP=0xff
 * P= 0x04 (Reserved|B|D|I) = (0|0|0|1)
 */
	sb	zero, OFFSET_A(WORK)
	sb	zero, OFFSET_X(WORK)
	sb	zero, OFFSET_Y(WORK)
	li	t0, 0xff
	sb	t0, OFFSET_SP(WORK)
	sb	zero, OFFSET_WORK_Z(WORK)
	sb	zero, OFFSET_WORK_C(WORK)
	sb	zero, OFFSET_WORK_V(WORK)
	sb	zero, OFFSET_WORK_N(WORK)
	li	t0, 0x04
	sb	t0, OFFSET_WORK_RBDI(WORK)

/* 起動時 PC
 * (0xfffc,0xfffd) は prgrom + prgsize - (4,3) の番地の内容から。
 */
	lw	t1, OFFSET_PRGROM(WORK)
	lw	t2, OFFSET_PRGSIZE(WORK)
	addu	t1, t1, t2
	lbu	t3, -4(t1)
	lbu	t4, -3(t1)
	sll	t4, t4, 8
	or	t3, t3, t4
	sh	t3, OFFSET_PC(WORK)
	move	v0, t3

/* chr bank register の設定
 * (chrrom read など、bank 切り替えなしでも必要)
 * 0000, 0001, ..., 0007
 */
	move	t0, WORK
	addiu	t1, t0, 2 * 8
	li	t2, 0
1:
	sh	t2, OFFSET_CHRBANK_REG0000(t0)
	addiu	t0, t0, 2
	addiu	t2, t2, 1
	bne	t0, t1, 1b

/* chrrom read 時に chr seg 参照しているので bank 切り替えなしでも
 * この初期化は必要。
 * sun4_vrom の初期化も必要。
 * MMC5_BR7 >= 0x80 であることを利用して MMC5 かどうか判別する。
 */
	sh	zero, OFFSET_CHRSEG_FOR_READ(WORK)
	sb	zero, OFFSET_SUN4(WORK)
	sb	zero, OFFSET_MMC5_BR7(WORK)

/* ライン割り込みの禁止 */
	li	t0, 0x8000
	sh	t0, OFFSET_LINECNT_IRQ(WORK)
	sh	t0, OFFSET_VRC4_LINECNT_SAVE(WORK)
	sh	zero, OFFSET_LINECNT_IRQ_PRESET(WORK)
	sb	zero, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	la	t1, linecnt_over00
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	
/* マッパー依存の 初期化
 * テーブルのバイト数は ずれていないか ?
 * header[7] の upper 4bits + header[6] の upper 4bits がマッパー番号。
 */
	lw	t2, OFFSET_HEADERP(WORK)
	la	a0, store_functbl_mapper0
#ifdef KS_RFC_MAPPER_EXTENSION
	la	t1, store_functbl_mapper256
	subu	t1, t1, a0
	addiu	t1, t1, -(4 * 5 * 0x100)
	beq	t1, zero, 1f
	TRAP("MAPPER TBL BROKEN (T1)");
1:
#endif
	lbu	t1, 7(t2)
	lbu	t0, 6(t2)
	andi	t1, t1, 0xf0
	srl	t0, t0, 4
	or	t0, t0, t1
	mul	t1, t0, 4 * 5
	beq	t0, zero, 10f  /* mapper none */

	addu	a0, a0, t1
#ifndef KS_RFC_MAPPER_EXTENSION
/* mapper 0xfd は disksys by myself
 */
	xori	t2, t0, 0xfd
	bne	t2, zero, 1f
	la	a0, store_functbl_mapper253
1:
#endif
/* t0-t3 は固定。飛び先で参照することもある */
	lw	t4, 0(a0)
	lw	t0, 4(a0)
	lw	t1, 8(a0)
	lw	t2, 0xc(a0)
	lw	t3, 0x10(a0)
	sw	t0, OFFSET_STOREFUNC_TBL + 0x10(WORK)
	sw	t1, OFFSET_STOREFUNC_TBL + 0x14(WORK)
	sw	t2, OFFSET_STOREFUNC_TBL + 0x18(WORK)
	sw	t3, OFFSET_STOREFUNC_TBL + 0x1c(WORK)
	beq	t4, zero, 10f
	jalr	t4
mapper_init_func_done:
10:
	
#if 0
/* debug
 * set watchpoint
 * address &= 0xfffff8;
 * d1=1: read watch, d0=1: write watch
 */
	.set	noreorder
	addiu	t0, WORK, OFFSET_MMC1_BIGROM
	li	t1, 0xfffff8
	and	t0, t0, t1
	ori	t0, t0, 1
	mtc0	t0, C0_WATCHLO
	.set	reorder
#endif

/* PPU パレットデータ初期値を 黒 (0x0f) に
 */
	move	t0, WORK
	addiu	t1, t0, 0x20
	li	t2, 0x0f0f0f0f
/*	li	t2, 0x01010101*/
/*	li	t2, 0x0e0e0e0e*/
1:
	sw	t2, OFFSET_PALETTE_BUF(t0)
	addiu	t0, t0, 4
	bne	t0, t1, 1b

/* 最後に、デルタモジュレーション テーブルアドレス設定
 * page table for 0xc000 + 0xc000
 */
	lw	t0, OFFSET_PAGE_TABLE + 0x18(WORK)
	li	t1, 0xc000
	addu	a0, t0, t1
	jal	Sound_SetC000

	lw	s0, OFFSET_STACK_S0(sp)
	lw	s1, OFFSET_STACK_S1(sp)
	lw	s2, OFFSET_STACK_S2(sp)
	lw	s3, OFFSET_STACK_S3(sp)
	lw	s4, OFFSET_STACK_S4(sp)
	lw	s5, OFFSET_STACK_S5(sp)
	lw	s6, OFFSET_STACK_S6(sp)
	lw	s7, OFFSET_STACK_S7(sp)
	lw	gp, OFFSET_STACK_GP(sp)
	lw	fp, OFFSET_STACK_FP(sp)
	lw	ra, OFFSET_STACK_RA(sp)

	addiu	sp, sp, EMU_RFC_ASM_STACKSIZE
	jr	ra

	GLOBAL(init01)
init01:
/* MMC1
 * r0-r3 初期値は 0x3f ということで、通常に書き込んだ値と同じにならない。
 * ... あかん、初期値依存の hal/satsui_k.nes が ...
 * BBRAM を使えるようにしておく (kid_icar.nes)
 * dq4j.nes type (prgsize == 0x80000) の場合、page table が狂うので
 * c000-, e000- の page table は -0x40000 する。
 * 決めうちでいいよね。
 */
	li	t0, 5
	sb	t0, OFFSET_MMC1_SHIFTCOUNT(WORK)
	li	t0, 0x0f
	sb	t0, OFFSET_MMC1_R0(WORK)
	li	t0, 0x00
	sb	t0, OFFSET_MMC1_R1(WORK)
	sb	t0, OFFSET_MMC1_R2(WORK)
	sb	t0, OFFSET_MMC1_R3(WORK)
	sb	zero, OFFSET_MMC1_ROMBANK(WORK)
	sb	zero, OFFSET_MMC1_RAMBANK(WORK)
	sw	zero, OFFSET_MMC1_BIGROM(WORK)
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)
	lw	t1, OFFSET_PRGSIZE(WORK)
	li	t2, 0x80000
	bne	t2, t1, 1f
/* MMC1 リセットの時のことがあるので、prgsize を 0x40000 にする */
	li	t2, 0xff
	sb	t2, OFFSET_MMC1_DQ4_MODE(WORK)
	li	t2, 0x40000
	sw	t2, OFFSET_PRGSIZE(WORK)
	lw	t1, OFFSET_PAGE_TABLE + 0x18(WORK)
	subu	t0, t1, t2
	sw	t0, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x1c(WORK)
1:
/* chr bank table の初期化 */
	move	t0, WORK
	addiu	t1, t0, 2 * 8
	li	t2, 0
1:
	sh	t2, OFFSET_CHRBANK_REG0000(t0)
	addiu	t2, t2, 1
	addiu	t0, t0, 2
	bne	t0, t1, 1b

	jr	ra
	
	GLOBAL(init02)
init02:
/* PRG1M, fixC000
 * 8000-ffff の書き込みで 8000-bfff の pbank
 * 2Mbit もある。
 * BBRAM を使えるようにしておく (salaman0.nes)
 */
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)
	jr	ra
	
	GLOBAL(init03)
init03:
/* CHR128/256
 * 6000-ffff の書き込みで d0,d1 が chr bank になる。
 */
	sw	t0, OFFSET_STOREFUNC_TBL + 0xc(WORK)
	la	t0, chrbank_func03
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	jr	ra

	GLOBAL(init04)
init04:
/* MMC3
 * ヘッダに関係なく BBRAM は使えるようにしておく (smario3.nes)
 */
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)
	sb	zero, OFFSET_MMC3_POINTER(WORK)
	sw	zero, OFFSET_MMC3_R0(WORK)
	sh	zero, OFFSET_MMC3_R6(WORK)
	la	t1, chrbank_func04
	sw	t1, OFFSET_CHRBANK_FUNCP(WORK)
	la	t1, linecnt_over04
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	jr	ra

	GLOBAL(init05)
init05:
/* MMC5
 * amode=3
 * (pamode は 不定)
 * br7=0xff
 * pbrex=0
 * ヘッダに関係なく BBRAM は使えるようにしておく (o/koei/sangok2j.nes)
 * linecnt_irq cause bit のクリア
 */
	sb	zero, OFFSET_MMC5_EXRAM + 0x400 + 4(WORK)
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)
/* debug for ohotsuku.nes (converted for MMC5)
 */
	la	t0, store_verbose
	sw	t0, OFFSET_STOREFUNC_TBL + 0x10(WORK)
	sw	t0, OFFSET_STOREFUNC_TBL + 0x14(WORK)
	sw	t0, OFFSET_STOREFUNC_TBL + 0x18(WORK)
	
	li	t1, 0x0300
	sh	t1, OFFSET_MMC5_AMODE_PAMODE(WORK)
/* amode 3 の mapping 設定 */

	li	t1, 0xff
	sb	t1, OFFSET_MMC5_BR7(WORK)

	sh	zero, OFFSET_MMC5_PBREX(WORK)
	
	la	t0, store05_io
	sw	t0, OFFSET_STOREFUNC_TBL + 0x08(WORK)

/* storefunc の初期化 */
	la	t0, mmc5_store_functbl_org
	addiu	t1, WORK, OFFSET_MMC5_STOREFUNC_TBL
	addiu	t2, t0, 4 * 0x34
1:
	ld	t3, 0(t0)
	ld	t4, 8(t0)
	sd	t3, 0(t1)
	sd	t4, 8(t1)
	addiu	t0, t0, 0x10
	addiu	t1, t1, 0x10
	bne	t0, t2, 1b

	la	t1, chrbank_func05
	sw	t1, OFFSET_CHRBANK_FUNCP(WORK)
	la	t1, linecnt_over05
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	jr	ra
	
	GLOBAL(init06)
init06:
/* FFE F4xxx
 * debug とりあえず。
 * goemon1j.nes 対策なのだが restart address は rom offset +0x1e210
 * のようだ。
 * とりあえず prgrom + 0x1e000 を e000-ffff の page table に。
 *                   + 0x1c000 を c000-dfff の page table に。
 */
#if 0
/* mappers.nfo まったくうそやん。*/
	la	t0, store06_4000
	sw	t0, OFFSET_STOREFUNC_TBL + 0x08(WORK)
#endif
	
	lw	t1, OFFSET_PRGROM(WORK)
	li	t2, 0x1c000 - 0xc000
	addu	t0, t1, t2
	la	t3, chrbank_func03
	sw	t3, OFFSET_CHRBANK_FUNCP(WORK)
	j	inite3_setfffc_common

	GLOBAL(init07)
init07:
/* Rare
 * 1 画面モードだけ ?
 * first 32K ROM bank is swapped into $8000 when the cart is started or reset.
 */
	lw	t1, OFFSET_PRGROM(WORK)
	addiu	t1, t1, -0x8000
	sw	t1, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x1c(WORK)
	sh	zero, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	jr	ra
	
	GLOBAL(init09)
init09:
/* MMC2
 * a000-ffff が 固定
 * a000-bfff は prgrom + prgsize - 0x6000 - 0xa000 (rombank last - 2)
 * うみゅーこれは無理や。ていうかパンチアウトだけやし。
 * デモ見てたら 8000H に store してるぞ。(PC=b0a5)
 */
	lw	t0, OFFSET_PRGROM(WORK)
	lw	t1, OFFSET_PRGSIZE(WORK)
	li	t2, 0x6000 + 0xa000
	addu	t0, t0, t1
	subu	t0, t0, t2
	sw	t0, OFFSET_PAGE_TABLE + 0x14(WORK)

	sb	zero, OFFSET_MMC3_POINTER(WORK)
	la	t1, chrbank_func04 /* とりあえず */
	sw	t1, OFFSET_CHRBANK_FUNCP(WORK)
	jr	ra

	GLOBAL(init0f)
init0f:
/* 100 in 1
 *  Notes: - The first 32K of ROM is loaded into $8000 on reset. There is
 *            8K of VRAM at PPU $0000.
 */
	lw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	j	inite3_setfffc_common

	GLOBAL(init12)
init12:
/* Jaleco SS8806
 * BBRAM 使用可能に (toppro.nes)
 */
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	la	t1, linecnt_over12
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	sh	zero, OFFSET_SS_PBANK0 + 0x0(WORK)
	sh	zero, OFFSET_SS_PBANK0 + 0x2(WORK)
	sh	zero, OFFSET_SS_PBANK0 + 0x4(WORK)
	sh	zero, OFFSET_SS_CHRBANK0 + 0x0(WORK)
	sh	zero, OFFSET_SS_CHRBANK0 + 0x2(WORK)
	sh	zero, OFFSET_SS_CHRBANK0 + 0x4(WORK)
	sh	zero, OFFSET_SS_CHRBANK0 + 0x6(WORK)
	sh	zero, OFFSET_SS_CHRBANK0 + 0x8(WORK)
	sh	zero, OFFSET_SS_CHRBANK0 + 0xa(WORK)
	sh	zero, OFFSET_SS_CHRBANK0 + 0xc(WORK)
	sh	zero, OFFSET_SS_CHRBANK0 + 0xe(WORK)
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)
	jr	ra

	GLOBAL(init13)
init13:
/* Namcot 106 */
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	la	t1, linecnt_over13
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	la	t0, store13_io
	sw	t0, OFFSET_STOREFUNC_TBL + 0x08(WORK)
#if 0
/* 7800 に 書き込んでいる namco/fcirc91.nes - BBRAM */
	la	t0, store_verbose
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
#endif
/* 横持ちで 初期化 */
	li	t0, 0x0001
	sh	t0, OFFSET_SS_PBANK0(WORK)
	sh	t0, OFFSET_SS_PBANK0 + 2(WORK)
#if 0
/* n_clasic.nes, mindseek.nes が止まるのは関係ない。*/
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)
#endif
	jr	ra

	GLOBAL(init16)
init16:
/* mapper 0x16=Konami VRC2-Shift
 * BBRAM 使用可能にしておく ?
 */
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	jr	ra

	GLOBAL(init19)
init19:
init17:
init15:
/* mapper 0x19=VRC2/4 Even
 * mapper 0x18=VRC6-A からも来る。
 * mapper 0x17=VRC2/4 Odd
 * mapper 0x15=VRC4
 * BBRAM は使えるようにしておこう。
 * linecnt irq は 単発のみ
 */
	la	t1, linecnt_over19
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
init19_common:
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)
	sb	zero, OFFSET_VRC4_MAPMODE(WORK)
	sb	zero, OFFSET_VRC4_PBANK(WORK)
	jr	ra

	GLOBAL(init18)
init18:
/* mapper 0x18=VRC6-A
 *        0x1a=VRC6-B
 */
	sb	zero, OFFSET_VRC6_REG_SWAP(WORK)
	sb	zero, OFFSET_VRC6_IRQ_SELECT(WORK)
	li	t0, 1
	sb	t0, OFFSET_VRC6_IRQ_REG(WORK)
	GLOBAL(init18_common)
init18_common:
	la	t1, linecnt_over18
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	sb	zero, OFFSET_VRC6_IRQ(WORK)
	j	init19_common

	GLOBAL(init1a)
init1a:
	li	t0, 0x03
	sb	t0, OFFSET_VRC6_REG_SWAP(WORK)
	li	t1, 0x01
	sb	t1, OFFSET_VRC6_IRQ_SELECT(WORK)
	li	t0, 2
	sb	t0, OFFSET_VRC6_IRQ_REG(WORK)
	j	init18_common

	GLOBAL(init20)
init20:
/* Irem G-101
 * ss_pbank0 を pbank, + 1 を mapmode がわりに。
 */
	sh	zero, OFFSET_SS_PBANK0(WORK)
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	jr	ra
	
	GLOBAL(init21)
init21:
/* Taito TC0190
 */
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	la	t1, linecnt_over21
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	jr	ra
	
	GLOBAL(init42)
init42:
/* GNROM
 * xxPPCCCC
 * PP: pbank @ 8000-ffff
 * CC: cbank @ 0000-1fff
 * c000-ffff の page table を 8000-bfff と同じものにしておく。
 * jaleco/yokaiclb.nes が 6000 に書き込んでいるが同等に扱う。
 * jaleco/dan.nes は chrrom 0x20000 bytes.
 */
	lw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x1c(WORK)
	la	t1, chrbank_func04
	sw	t1, OFFSET_CHRBANK_FUNCP(WORK)
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	jr	ra

	GLOBAL(init43)
init43:
/* Sunsoft 3
 * fantasy zone 2 だけ ?
 *
 */
	la	t1, chrbank_func04
	sw	t1, OFFSET_CHRBANK_FUNCP(WORK)
	la	t1, linecnt_over43
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	jr	ra
	
	GLOBAL(init44)
init44:
/* Sunsoft 4
 * BBRAM は使えるようにしておく
 * VROM 対策で internal format attr data を convert する。
 * (chrrom 00000-3ffff の attr を 40000-7ffff に)
 * sun4 を 0x40 にしておく。
 */
	la	t0, chrbank_func44
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)
	li	t0, 0x40
	sb	t0, OFFSET_SUN4(WORK)
	sb	zero, OFFSET_SUN4_VROM_ENABLED(WORK)

/* t3: chrrom src
 * t4: chrrom src true end
 * t5:
 * t6: chrrom dst base
 * t7: src offset 0..0x40
 * t8: chrrom dst work
 */
	lw	t3, OFFSET_CHRROM(WORK)
	li	t0, 0x40000
	addu	t4, t3, t0
	addu	t6, t3, t0
2:
	li	t7, 0
1:
	lbu	t2, 0x3c0(t3)
	andi	t1, t7, 0x07
	sll	t1, t1, 2     /* 0x07 -> 0x1c */

	andi	t0, t7, 0x38
	sll	t0, t0, 4     /* 0x38 -> 0x380 */
	or	t0, t0, t1
	addu	t8, t6, t0
/* (d7,d6) -> draw vram + (0x40 と 0x60) + (2 と 3) */
	andi	t1, t2, 0xc0
	sb	t1, 0x42(t8)
	sb	t1, 0x43(t8)
	sb	t1, 0x62(t8)
	sb	t1, 0x63(t8)
/* (d5,d4) -> draw vram + (0x40 と 0x60) + (0 と 1) */
	andi	t0, t2, 0x30
	sll	t0, t0, 2
	sb	t0, 0x40(t8)
	sb	t0, 0x41(t8)
	sb	t0, 0x60(t8)
	sb	t0, 0x61(t8)
/* (d3,d2) -> draw vram + (0x00 と 0x20) + (2 と 3) */
	andi	t1, t2, 0x0c
	sll	t1, t1, 4
	sb	t1, 0x02(t8)
	sb	t1, 0x03(t8)
	sb	t1, 0x22(t8)
	sb	t1, 0x23(t8)
/* (d1,d0) -> draw vram + (0x00 と 0x20) + (0 と 1) */
	sll	t1, t2, 6
	sb	t1, 0x00(t8)
	sb	t1, 0x01(t8)
	sb	t1, 0x20(t8)
	sb	t1, 0x21(t8)

	addiu	t7, t7, 1
	addiu	t3, t3, 1
	sltiu	t0, t7, 0x40
	bne	t0, zero, 1b

	addiu	t3, t3, 0x3c0
	addiu	t6, t6, 0x400
	bne	t3, t4, 2b
	
	jr	ra
		
	GLOBAL(init45)
init45:
/* Sunsoft 5
 */
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	la	t1, linecnt_over45
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)
	jr	ra

	GLOBAL(init46)
init46:
/* Kitarou2 mapper
 * taito/arkanoi2.nes など。
 */
	la	t1, chrbank_func04
	sw	t1, OFFSET_CHRBANK_FUNCP(WORK)
	jr	ra
	
	GLOBAL(init49)
init49:
/* Konami VRC3 (salamder.nes)
 * このゲーム WRAM 載せてるよ。
 * 8000,9000 は chrbank でしょう。chrram なので無視。
 * f000 が pbank @ 8000-bfff くさい。
 */
	la	t1, linecnt_over49
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	jr	ra
	
	GLOBAL(init56)
init56:
/* Jaleco Moepro
 * prg は動いた。
 * chr も動いた。
 */
	la	t1, store56_6000
	sw	t1, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	
	la	t0, store_verbose
	sw	t0, OFFSET_STOREFUNC_TBL + 0x10(WORK)
	sw	t0, OFFSET_STOREFUNC_TBL + 0x14(WORK)
	sw	t0, OFFSET_STOREFUNC_TBL + 0x18(WORK)
	sw	t0, OFFSET_STOREFUNC_TBL + 0x1c(WORK)

	la	t0, chrbank_func03
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	
	jr	ra

	GLOBAL(init58)
init58:
/* Namcot 118
 * かなり MMC3 に似ている。
 * c000=0 書き込み (devilman.nes)
 */
	la	t0, store_verbose
	sw	t0, OFFSET_STOREFUNC_TBL + 0x18(WORK)
	
	sb	zero, OFFSET_MMC3_POINTER(WORK)
	sw	zero, OFFSET_MMC3_R0(WORK)
	sh	zero, OFFSET_MMC3_R6(WORK)
	la	t1, chrbank_func04
	sw	t1, OFFSET_CHRBANK_FUNCP(WORK)
	la	t1, linecnt_over04
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	jr	ra

	
	GLOBAL(init5a)
init5a:
/* PC-JY-? mapper
 * o/cracked/tekken2.nes, o/cracked/samuspi2.nes
 */
	la	t0, store_verbose
	sw	t0, OFFSET_STOREFUNC_TBL + 0x1c(WORK)
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	la	t1, linecnt_over5a
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	jr	ra

	GLOBAL(init5c)
init5c:
/* Jaleco Moepro88, MoeproSoccer
 */
	la	t0, store_verbose
	sw	t0, OFFSET_STOREFUNC_TBL + 0x10(WORK)
	sw	t0, OFFSET_STOREFUNC_TBL + 0x14(WORK)
	sw	t0, OFFSET_STOREFUNC_TBL + 0x18(WORK)
	sw	t0, OFFSET_STOREFUNC_TBL + 0x1c(WORK)
	jr	ra
	
	GLOBAL(init63)
init63:
/* VS Unisystem
 * debug とりあえず vspack/iceclimv.nes で実験。
 * scroll を V に。
 */
	li	t0, 0x800
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	la	t0, store63_4020
	sw	t0, OFFSET_STORE_IO_FUNCTBL + 0x20 * 4(WORK)
	jr	ra

	GLOBAL(inite0)
inite0:
/* 72 in 1
 * 110 in 1 の簡易版
 */
	li	t0, 0x400
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	lw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	j	inite3_setfffc_common

	GLOBAL(inite1)
inite1:
/* 110 in 1
 * reset 時 横持ち、0x8000 bytes/bank, prg bank 0, chr bank 0,
 * 5800-5803 = 0x0f, 0x00, 0x0f, 0x00
 * 4bit register files x 4 内蔵ということなので、それは BBRAM を使う。
 */
	li	t0, 0x400
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)

	li	t2, 0x0f000f00
	sw	t2, OFFSET_BBRAM(WORK)
	
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)

	la	t1, storee1_io
	sw	t1, OFFSET_STOREFUNC_TBL + 0x08(WORK)
	la	t2, loade1_io
	sw	t2, OFFSET_LOADFUNC_TBL + 0x08(WORK)
	
	lw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	j	inite3_setfffc_common

	GLOBAL(inite2)
inite2:
inite5:
/* 0xe2 = 76 in 1
 * 0xe5 = 31 in 1
 */
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	sh	zero, OFFSET_SS_PBANK0(WORK)
	lw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	j	inite3_setfffc_common

	GLOBAL(inite3)
inite3:
/* 1200in1
 * reset 時 横持ち、chrram, 8000-, c000- ともに rombank 0
 */
	lw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	addiu	t0, t0, -0x4000

	GLOBAL(inite3_setfffc_common)
inite3_setfffc_common:
/* t0: page table @ e000-
 */
	sw	t0, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x1c(WORK)
	li	t2, 0xfffc
	addu	t3, t0, t2
	lbu	t0, 0(t3)
	lbu	t1, 1(t3)
	sll	t1, t1, 8
	or	v0, t0, t1
	sh	v0, OFFSET_PC(WORK)
	
	jr	ra

	GLOBAL(inite6)
inite6:
/* 22 in 1
 * 最初 contra mode で、 power off によって
 * 21 in 1/contra mode が swap するらしい。
 */
	la	t0, chrbank_func04
	sw	t0, OFFSET_CHRBANK_FUNCP(WORK)
	sh	zero, OFFSET_SS_PBANK0(WORK)
	lw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	j	inite3_setfffc_common

	GLOBAL(initqd)
initqd:
/* disksys by myself (mapper 0xfd)
 * BBRAM は使えるようにしておく。
 * prgrom 領域にも書き込める。
 */
	la	t0, store_bbram_64k
	sw	t0, OFFSET_STOREFUNC_TBL + 0x0c(WORK)
	la	t0, load_6000
	sw	t0, OFFSET_LOADFUNC_TBL + 0x0c(WORK)

	la	t1, linecnt_overqd
	sw	t1, OFFSET_LINECNT_OVER_FUNCP(WORK)
	jr	ra

/* リセットルーチン終了 */
	.end	ks_reset_rfcstate_asm


	
/* extern int ks_emu_rfc_asm(rfcstate_object *p);
 */
	.globl	ks_emu_rfc_asm
	.ent	ks_emu_rfc_asm
	.frame	sp, EMU_RFC_ASM_STACKSIZE, ra
ks_emu_rfc_asm:
	addiu	sp, sp, -EMU_RFC_ASM_STACKSIZE
	sw	s0, OFFSET_STACK_S0(sp)
	sw	s1, OFFSET_STACK_S1(sp)
	sw	s2, OFFSET_STACK_S2(sp)
	sw	s3, OFFSET_STACK_S3(sp)
	sw	s4, OFFSET_STACK_S4(sp)
	sw	s5, OFFSET_STACK_S5(sp)
	sw	s6, OFFSET_STACK_S6(sp)
	sw	s7, OFFSET_STACK_S7(sp)
	sw	gp, OFFSET_STACK_GP(sp)
	sw	fp, OFFSET_STACK_FP(sp)
	sw	ra, OFFSET_STACK_RA(sp)

	move	WORK, a0

/* パッドデータの変換 ... famicom.c でやっていた。 */

/* SP が ucode_wk へ DMA copy し終わっていなければ待つ。
 */
	lw	t1, OFFSET_WORK_RSPSIGNAL(WORK)
	bne	t1, zero, 1f  /* ハンドシェイクを skip */
	lui	t0, 0xa404
2:
	.set	noreorder
	andi	t2, t1, 0x0400
	beq	t2, zero, 2b
	lw	t1, 0x10(t0)
	.set	reorder
/* IO_WRITE(SP_STATUS_REG, SP_CLR_RSPSIGNAL); */
	ori	t1, zero, 0x8000
	sw	t1, 0x10(t0)
	sw	t2, OFFSET_WORK_RSPSIGNAL(WORK) /* t2 != 0 */
1:

	lhu	PC, OFFSET_PC(WORK)
	lbu	A, OFFSET_A(WORK)
	lbu	X, OFFSET_X(WORK)
	lbu	Y, OFFSET_Y(WORK)
	lbu	SP, OFFSET_SP(WORK)
	lbu	Z, OFFSET_WORK_Z(WORK)
	lbu	C, OFFSET_WORK_C(WORK)
	lbu	V, OFFSET_WORK_V(WORK)
	lbu	N, OFFSET_WORK_N(WORK)

	lw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	lw	JUMPTBLP, OFFSET_THREAD_JUMPTBLP(WORK)
	lw	PAGE_PTR, OFFSET_THREAD_PAGE_PTR(WORK)
	lw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	lw	ra, OFFSET_THREAD_RA(WORK)
	lw	a2, OFFSET_THREAD_PC(WORK)
	jr	a2

/* debug */
	GLOBAL(emu_line0)
emu_line0:
/* ラベルとは裏腹に、ここは line 0 ではない。
 * line -22 である。
 * palette change count, bg bank change count, obj bank change count,
 * oam change count を 0 に。
 */
	sb	zero, OFFSET_PALETTE_CHANGE_COUNT(WORK)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_CHANGE_COUNT(WORK)
	sb	zero, OFFSET_OAM_CHANGE_COUNT(WORK)

/* 今回 書き込む gfxbuf を gfx_selbuf から決定する。
 * ダブルバッファ。
 */
	lbu	t0, OFFSET_GFX_SELBUF(WORK)
	lbu	t2, OFFSET_GFX_SELBUF_NEXT(WORK)
	li	t1, OFFSET_GFXBUF
	bne	t0, zero, 1f
	li	t1, OFFSET_GFXBUF2
1:
	sb	t2, OFFSET_GFX_SELBUF(WORK)
	addu	t1, t1, WORK
	sw	t1, OFFSET_GFXPTR(WORK)

/* chr touch flag をクリアする。
 */
	move	t0, WORK
	addiu	t1, t0, SIZEOF_CHR_TOUCH_FLAG
	li	t2, -1  /* 0xffffffff */
1:
	sw	t2, OFFSET_GFXBUF + OFFSET_GFXBUF_CHR_TOUCH_FLAG(t0)
	addiu	t0, t0, 4
	bne	t0, t1, 1b

	j	mainloop_srlt0
	
	GLOBAL(line_240_done)
line_240_done:
/* PPU 2002H output の d7 を 1 にするのは、NMI 起動よりほんの少し前
 * のようだ。(ascii/castle_j.nes 0x816a 番地)
 */
	lbu	t2, OFFSET_PPU2000H_OUTPUT + 2(WORK)
	ori	t2, t2, 0x80
/* strikef のクリア */
	andi	t2, t2, 0xbf
	sb	t2, OFFSET_PPU2000H_OUTPUT + 2(WORK)
	li	t1, 0xff
	sb	t1, OFFSET_STRIKEF_LINE(WORK)
	
	li	t1, -22
	sh	t1, OFFSET_LINECNT(WORK)
	lw	t0, OFFSET_EMUFRAME(WORK)
	addiu	t0, t0, 1
	sw	t0, OFFSET_EMUFRAME(WORK)

#if 0
/* debug */
	lw	a1, OFFSET_EMUFRAME(WORK)
	PRINTF("emuframe=%x\n");
#endif

/* draw list の next line パラメタをセットする。
 * これは、「同じ draw BG bank」が あと何 line 後に現れるかを
 * 指定するもので、なければ 0 のまま。
 * t0: draw BG bank master
 * t1:              work
 * t2: draw list offset top (-240,-239,-238, ..., 1)*8
 * t3: draw list offset work
 * t4: process count (240,239,...,0)
 * t5: gfxptr
 * t6: t5+t2 or t5+t3
 * Sunsoft 4 なら 無視。
 */
	lbu	t2, OFFSET_SUN4(WORK)
	lw	t5, OFFSET_GFXPTR(WORK)
	bne	t2, zero, set_draw_next_loop_done
#define DRAW_LIST_LOOP_BEGIN (240)*8
	li	t2, -(DRAW_LIST_LOOP_BEGIN)
	li	t4, 240
set_draw_next_loop_1:
	addu	t6, t5, t2
	lbu	t0, OFFSET_GFXBUF_DRAW_LIST + OFFSET_GFX_DRAW_BG_BANK + DRAW_LIST_LOOP_BEGIN(t6)
	addiu	t2, t2, 8
	move	t3, t2
set_draw_next_loop_2:
	addu	t6, t5, t3
	lbu	t1, OFFSET_GFXBUF_DRAW_LIST + OFFSET_GFX_DRAW_BG_BANK + DRAW_LIST_LOOP_BEGIN(t6)
	addiu	t3, t3, 8
	bgtz	t3, 2f

	bne	t1, t0, set_draw_next_loop_2
	j	1f
2:
/* この draw BG bank を使う line は 後にはなかった。*/
	addiu	t4, t4, -1
	bne	t4, zero, set_draw_next_loop_1
	j	set_draw_next_loop_done
1:
	subu	t1, t3, t2
	srl	t1, t1, 3
/* これで 何ライン後に 同じ draw BG bank を使っているかが出た。
 * t2 はすでに +8 されているので sb のオフセットで調整。
 */
	addu	t6, t5, t2
	sb	t1, OFFSET_GFXBUF_DRAW_LIST + OFFSET_GFX_NEXT_LINE + DRAW_LIST_LOOP_BEGIN - 8(t6)
	addiu	t4, t4, -1
	bne	t4, zero, set_draw_next_loop_1
set_draw_next_loop_done:
	
	
/* 次は emu_line0 から。*/
	la	a2, emu_line0
	j	emuframe_done

/* fetch opcode
 * address < 0x6000 なら load 関数。
 */
	GLOBAL(mainloop_srlt0)
mainloop_srlt0:
	srl	t0, PC, 13
	
	GLOBAL(mainloop)
mainloop:
	sll	t1, t0, 2
	addu	PAGE_PTR, t1, WORK
	sltiu	t2, t0, 0x6000 >> 13
	beq	t2, zero, 1f
/* address < 0x6000 */
	lw	t0, OFFSET_LOADFUNC_TBL(PAGE_PTR)
	la	ra, 2f
	move	a0, PC
	jr	t0
1:
/* address >= 6000 */
	lw	t0, OFFSET_PAGE_TABLE(PAGE_PTR)
/* どっちみち delay slot なので ... */
	andi	PC, PC, 0xffff
	addu	t3, t0, PC
	lbu	v0, 0(t3)
2:
	sll	t0, v0, 4
	addu	JUMPTBLP, t0, WORK
/* func1 と nbytes, cycles の読み出し */
	lw	t4, OFFSET_JUMPTBL + 0(JUMPTBLP)
	lbu	t5, OFFSET_JUMPTBL + 0xc(JUMPTBLP) /* nbytes */
	lhu	t6, OFFSET_JUMPTBL + 0xe(JUMPTBLP) /* cycles */

#if 0
#ifdef KS_HISTORY
/* debug history, デバッグ debug PC history このブロックは reorder させない */
1:
	lbu	v1, OFFSET_PC_HIST_OFFSET(WORK)
	li	a2, OFFSET_PC_HIST_BUF
	addu	a2, a2, v1
	addiu	v1, v1, 2
	sb	v1, OFFSET_PC_HIST_OFFSET(WORK)
	addu	a2, a2, WORK
	sh	PC, 0(a2)
1:
#endif
#endif

#if 0
/* debug */
	lw	a1, OFFSET_WRAM + 0x700(WORK)
	PRINTF("%08x\n");
#endif

#if 0
#ifdef KS_TRACE
/* debug trace
 * v0: opcode, t4: funcptr, t5: nbytes, t6: cycles
 * t3: opcode pointer (PC >= 0x6000 のときのみ)
 * NV1BDIZC AXY=00 00 00  SP=1xx  00:0000 00 0000  lda #c000
 * CONT_R = 0x0010, CONT_L = 0x0020
 */

#if 0
/* exbike_.nes line 30-100 を trace */
	lw	t0, OFFSET_EMUFRAME(WORK)
	sltiu	t1, t0, 600
	bne	t1, zero, 99f
	lh	t0, OFFSET_LINECNT(WORK)
	slti	t1, t0, 30
	bne	t1, zero, 99f
	slti	t1, t0, 100
	beq	t1, zero, 99f
#endif

#if 0
/* zelda.nes で起動せず。audio routine で暴走する。
 * おそらく RSP が audio のワークかコードを破壊しているのだ。
 * 監視。
 */
	la	t0, debug_ptr
	lw	t1, 0(t0)
	lw	t0, 0(t1)
	tge	t0,zero
	lw	t0, OFFSET_TRACE_FLAG(WORK)
	beq	t0, zero, 99f
#endif

#if 0
/* zelda.nes で起動しなかったのと、似た症状。
 * 今回(動物の森対応の途中)、chrram じゃないのに cluclu.nes で暴走する。
 * EmuSound_Start(u8 *noise); の 引数 a0 がいつのまにか破壊されている。
 * ... 今回は、EmuSound_Start() を二重呼びしていたのが原因だった。
 */
	li	t0, 0x800397e8
	lw	t1, 0(t0)
	tne	zero, zero
	b	99f
#endif

#if 0
	lw	t0, OFFSET_TRACE_FLAG(WORK)
	bne	t0, zero, 90f
	xori	t0, PC, 0xe887
	bne	t0, zero, 99f
	sw	PC, OFFSET_TRACE_FLAG(WORK)
#endif
#if 0
/* smario3.nes check */

	addiu	t0, PC, 0x10000 - 0xd5e3
	srl	t1, t0, 16
	bne	t1, zero, 10f
	addiu	t0, t0, 0x50
	srl	t1, t0, 16
	bne	t1, zero, 90f

10:
	addiu	t0, WORK, OFFSET_WRAM + 0x270
	lbu	t1, 0(t0)
	andi	t1, t1, 0xc0
	beq	t1, zero, 10f

	addiu	t0, t0, 8
	lbu	t1, 0(t0)
	andi	t1, t1, 0xc0
	beq	t1, zero, 10f
	
	addiu	t0, t0, 8
	lbu	t1, 0(t0)
	andi	t1, t1, 0xc0
	beq	t1, zero, 10f
	
	addiu	t0, t0, 8
	lbu	t1, 0(t0)
	andi	t1, t1, 0xc0
	bne	t1, zero, 99f
10:
	lbu	t1, 1(t0)
	xori	t1, t1, 0x51
	bne	t1, zero, 99f
	

	TRAP("?");
#endif

#if 0
/* mario94.nes check */
	addiu	t0, PC, 0x8000
	sltiu	t1, t0, 0x8b9f - 0x8000
	bne	t1, zero, 99f
	sltiu	t1, t0, 0x8bde - 0x8000
	beq	t1, zero, 99f
#endif
	
90:
	li	a1, 0x4e /* 'N' */
	li	a2, 0x56 /* 'V' */
	li	a3, 0x31 /* '1' */
	andi	t0, N, 0x80
	bne	t0, zero, 1f
	li	a1, 0x2d /* '-' */
1:
	bne	V, zero, 1f
	li	a2, 0x2d /* '-' */
1:
	lbu	t1, OFFSET_WORK_RBDI(WORK)
	andi	t0, t1, 0x20
	bne	t0, zero, 1f
	li	a3, 0x2d /* '-' */
1:
	PRINTF("%c%c%c");

	li	a1, 0x42 /* 'B' */
	li	a2, 0x44 /* 'D' */
	li	a3, 0x49 /* 'I' */
	andi	t0, t1, 0x10
	bne	t0, zero, 1f
	li	a1, 0x2d /* '-' */
1:
	andi	t0, t1, 0x08
	bne	t0, zero, 1f
	li	a2, 0x2d /* '-' */
1:
	andi	t0, t1, 0x04
	bne	t0, zero, 1f
	li	a3, 0x2d /* '-' */
1:
	PRINTF("%c%c%c");

	li	a1, 0x5a /* 'Z' */
	li	a2, 0x43 /* 'C' */
	move	a3, A
	andi	t0, Z, 0xff
	beq	t0, zero, 1f
	li	a1, 0x2d /* '-' */
1:
	bne	C, zero, 1f
	li	a2, 0x2d /* '-' */
1:
	PRINTF("%c%c AXY=%02x ");

	move	a1, X
	move	a2, Y
	move	a3, SP
	PRINTF("%02x %02x  SP=1%02x  ");

	li	a1, 0
	move	a2, PC
	move	a3, v0
	PRINTF("%02x:%04x %02x ");

/* 2byte 目以降の表示は address >= 0x6000 でないと 間違い。*/
	sltiu	t0, PC, 0x6000
	bne	t0, zero, 4f
6:
	addiu	t0, t5, -2
	bgtz	t0, 3f
	beq	t0, zero, 2f
	PRINTF("      ");
	li	a1, 0
	j	1f
4:
	sltiu	t0, PC, 0x2000
	beq	t0, zero, 5f
	andi	t3, PC, 0x7ff
#if OFFSET_WRAM
	addiu	t3, t3, OFFSET_WRAM
#endif
	addu	t3, t3, WORK
	j	6b	
5:
	PRINTF("????  ");
	li	a1, 0
	j	1f
2:
	lbu	a1, 1(t3)
	PRINTF("%02x    ");
	j	1f
3:
	lbu	a1, 1(t3)
	lbu	a2, 2(t3)
	PRINTF("%02x%02x  ");
	sll	a2, a2, 8
	or	a1, a1, a2
1:
	la	t0, static_debug_insttbl_ptr
	sll	t1, v0, 2
	addu	t0, t0, t1
	lw	a0, 0(t0)
/* ここは PRINTF マクロは使えない。*/
	sw	ra, OFFSET_STACK_RA_PRINTF(sp)
	jal	save_ossync_restore

	lw	a1, OFFSET_EMUFRAME(WORK)
	lh	a2, OFFSET_LINECNT(WORK)
	sra	a3, LINECYCLE, 8
	PRINTF("(%d:%03d.%+03d");
	
/* in H-blank ? */
	andi	t0, LINECYCLE, 0x01
	li	a1, 0x48 /* 'H' */
	bne	t0, zero, 1f
	li	a1, 0x5f /* '-' */
1:
	PRINTF("%c)\n");	
	
99:
	
#endif /* KS_TRACE */
#endif

	addu	LINECYCLE, LINECYCLE, t6
	addu	PC, PC, t5
#if 0
/* debug */
	teq	t5, 0x24
	teq	t4, zero
#endif

#if 0
#ifdef KS_DEBUG_VERBOSE
/* debug profile
 * 使ったことがある命令に対応する jumptbl に 0 を。
 */
	sb	zero, OFFSET_JUMPTBL + 0xd(JUMPTBLP)
#endif
#endif
	
	jalr	STORE_DONE, t4
	
	GLOBAL(next)
next:
	srl	t0, PC, 13
	bltz	LINECYCLE, mainloop

/* LINECYCLE の d0=1 なら H-blank 終了
 * set draw param
 */
	andi	t0, LINECYCLE, 0x01
	xor	LINECYCLE, LINECYCLE, t0
	beq	t0, zero, line_end
/* H-blank 終了
 * linecnt < 0 なら draw param, H-line 割り込みはなし。
 * line 240 の H-blank 終了なら draw param はなし。 linecnt = -22; H-line 割り込みチェックだけ。
 */
	addiu	LINECYCLE, LINECYCLE, LINECYCLE_CONST1

	lh	t4, OFFSET_LINECNT(WORK)
	addiu	t3, t4, -239

	bltz	t4, vblank    /* linecnt < 0 */

/* H-line 割り込みチェック は ここではやらない。*/

#if 0
	lbu	t0, OFFSET_STRIKEF_LINE(WORK)
	bgtz	t3, line_240_done

/* strikef */
	lbu	t1, OFFSET_PPU2000H_OUTPUT + 2(WORK)
	bne	t0, t4, 1f
	ori	t1, t1, 0x40
	sb	t1, OFFSET_PPU2000H_OUTPUT + 2(WORK)
1:
#endif
	lbu	t0, OFFSET_OAM + 0(WORK)
	bgtz	t3, line_240_done
#if 0
/* debug */
	lw	a0, OFFSET_EMUFRAME(WORK)
	sltiu	a0, a0, 420
	bne	a0, zero, 1f
	move	a1, t0
	move	a2, t4
	PRINTF("check strikef %x,%x\n");
1:
#endif
/* obj 0 の ypos と linecnt が等しくなったら strikef check
 * 今の obj chr bank を割り出し、linecnt + 1 + n が strikef_line になる。
 * t3: strikef_line (下で利用)
 */
	lbu	t3, OFFSET_STRIKEF_LINE(WORK)
	bne	t0, t4, 4f
#if 0
/* debug */
	move	a1, t3
	PRINTF("strikef_line");
#endif
	lbu	$1, OFFSET_MMC3_POINTER(WORK)
	lbu	t3, OFFSET_OAM + 1(WORK)
	andi	$1, $1, 0x80
	srl	$1, $1, 4  /* 0x80 -> 0x08 */
	lbu	t1, OFFSET_PPU2000H(WORK)
	andi	t2, t1, 0x20
	xor	t1, t1, $1
	beq	t2, zero, 20f  /* OBJ 8x8 */
/* 8x16 なら OBJ seg の xor を戻す。
 * chr code & 0x01 によって 0 or 0x08
 * chr code &= 0xfe;
 */
	xor	t1, t1, $1
	andi	$1, t3, 0x01
	sll	$1, $1, 3    /* 0x01 -> 0x08 */
	xor	t1, t1, $1
	andi	t3, t3, 0xfe
	li	t2, 1
20:
/* 8x8 なら t2 = 0, 8x16 なら t2 = 1 */
	move	$1, t2
	andi	t1, t1, 0x08
	addu	t1, t1, WORK
	andi	t2, t3, 0xc0
	srl	t2, t2, 5    /* 0xc0 -> 0x06 */
	addu	t1, t1, t2
	lhu	t1, OFFSET_CHRBANK_REG0000(t1)
	lw	t2, OFFSET_CHRROM(WORK)
	andi	t3, t3, 0x3f
	mul	t3, t3, 0x10
	mul	t1, t1, 0x400
	addu	t2, t2, t3
	addu	t2, t2, t1
/* $1: OBJ size によって 0 or 1
 * t0,t1: work
 * t2: chr data src ptr
 * t3: sub loop counter
 * t4: keep
 * a0: work
 */
	li	t3, 0
2:
	lbu	t0, 0(t2)
	lbu	t1, 8(t2)
	addiu	t3, t3, 1
	sltiu	a0, t3, 0x07
	or	t0, t0, t1
	bne	t0, zero, 8f
	addiu	t2, t2, 1
	bne	a0, zero, 2b
	addiu	t2, t2, 8
	addiu	$1, $1, -1
	beq	$1, zero, 2b
/* 透明 OBJ ?
 */
	addiu	t3, t4, 1
	j	5f
8:
/* linecnt + t3 が、strikef_linecnt になる。*/
	addu	t3, t3, t4
5:
	sb	t3, OFFSET_STRIKEF_LINE(WORK)
#if 0
/* debug */
	move	a1, t3
	PRINTF("=%x\n");
#endif
4:
/* t3: strikef_line であること。*/
	lbu	t1, OFFSET_PPU2000H_OUTPUT + 2(WORK)
	bne	t3, t4, 1f
	ori	t1, t1, 0x40
	
/* debug
 * itadaki.nes ブレ調整
 * 0x6 * 0x100 ではブレるので 0x8 * 0x100 にした
 * twinbee3.nes がブレる ... 0x2 * 0x100 なら こっちは OK なんだけど。
 */
#if 1
	addiu	LINECYCLE, LINECYCLE, 0x8 * 0x100
#endif
	
#if 0
/* debug */
	lw	t0, test_data
	addu	LINECYCLE, LINECYCLE, t0
#endif
	sb	t1, OFFSET_PPU2000H_OUTPUT + 2(WORK)
1:

/* draw param 設定 */
	sll	t4, t4, 3
	lw	t3, OFFSET_GFXPTR(WORK)
	addu	t4, t4, t3
/* PPU 2000H, 2001H はまとめて処理。
 * NEXT_LINE (初期値 0) と DRAW_SCCH もまとめて。
 * t4: gfxptr + linecnt * 8
 */
	lhu	t0, OFFSET_PPU2000H(WORK)
	lbu	t2, OFFSET_DRAW_SCCH(WORK)
	sh	t0, OFFSET_GFX_DRAW_2000H(t4)
	sh	t2, OFFSET_GFX_NEXT_LINE(t4)
/* t0: PPU 2000H, 2001H
 * debug: 不十分。
 * draw vram addr は | wrap | 0 | vram page | addr4-0 |
 * addr4-0 は * 0x20 するので vram [0x2000-0x23e0] に相当。
 * page は * 0x400, wrap は 1 なら ^ 0x400 する。
 * mirror mask and & 0x400 なら wrap = 1
 */
	lhu	t1, OFFSET_DRAW_SCCV(WORK)
	lhu	t2, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	lhu	t3, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	sll	t5, t1, 2
	and	t5, t5, t2
	or	t5, t5, t3
/* 0x800 or 0x400 -> 0x400 */
	srl	$1, t5, 1
	or	t5, t5, $1
	andi	t5, t5, 0x400
	srl	t5, t5, 5     /* 0x400 -> 0x20 */
	
	andi	$1, t2, 0x400
	srl	$1, $1, 3     /* 0x400 -> 0x80 */
	or	t5, t5, $1

	srl	t2, t1, 3     /* 0x3ff -> 0x7f */
	andi	t2, t2, 0x1f
	or	t5, t5, t2
	sb	t5, OFFSET_GFX_DRAW_VRAM_ADDR(t4)
#if 0
/* debug */
	lw	t5, OFFSET_EMUFRAME(WORK)
	sltiu	t5, t5, 300
	bne	t5, zero, 1f
	move	a1, t1
	lhu	a2, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	PRINTF("%x,%x\n");
1:
#endif
	
#if 0
/* debug */
	andi	t5, t5, 0x80
	tne	t5, zero
#endif
	andi	a1, t1, 0x07
	lw	t2, OFFSET_CHRBANK_FUNCP(WORK)
#if 0
	sb	a1, OFFSET_GFX_DRAW_BG_BANK(t4)
#endif
	
/* draw sccv は 0x000-0x0ef, 0x200-0x2ef, 0x000-...
 *           or 0x100-0x1ef, 0x300-0x3ef, 0x100-...
 * ただし 0xff->0x100 への繰り上がりは ない。(challeng.nes)
 */
	andi	$1, t1, 0x300
	addiu	t1, t1, 1
	andi	t1, t1, 0xff
	xori	t3, t1, 0xf0
	bne	t3, zero, 1f
	xori	$1, $1, 0x200
	li	t1, 0
#if 0
/* debug */
	PRINTF("xxxx\n");
#endif
1:
	or	t1, t1, $1
#if 0
/* debug */
	move	a2, a1
	lhu	t0, OFFSET_SERIAL4016D0(WORK)
	andi	t0, t0, 0x0010
	beq	t0, zero, 1f
	move	a1, t1
	PRINTF("SCCV=%x\n");
	move	a1, a2
1:
#endif
	sh	t1, OFFSET_DRAW_SCCV(WORK)

/* a1 に draw_sccv & 7, t4 に gfxptr, t2 に funcp
 * t0 = 0xff
 * t5 に bg_chrbank_touch_flag
 * chrbank 設定関数へ。
 * 終わったら mainloop_srlt0 へ。
 */
	lbu	t5, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	li	t0, 0xff
	jr	t2

/* chrbank functions
 */
	GLOBAL(chrbank_func_normal)
chrbank_func_normal:
/* ノーマル NES
 * BG chr bank は変わったか ?
 * chrbank 切り替えはないので PPU 2000H d4=0 なら bg bank index=0 とする。
 * よって 最高切り替え数チェックも無視。
 */
	lbu	t3, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK)
	lbu	t1, OFFSET_PPU2000H(WORK)
	bne	t5, zero, 10f
	sb	t0, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
/*	sltiu	t2, t3, BG_BANK_CHANGE_COUNT_MAX * 8 */
/*	beq	t2, zero, 10f */
/*	addiu	t3, t3, 8 */
/*	sb	t3, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK) */

	andi	t2, t1, 0x10   /* BG chr seg */
	srl	t3, t2, 1      /* 0x10 -> 0x08 */
	sb	t3, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK)

/* bg chr bank table もセットする。*/
	lw	t0, OFFSET_GFXPTR(WORK)
	addu	t0, t0, t3
	
	srl	t2, t2, 2      /* 0x10 -> 0x04 */
	sh	t2, OFFSET_GFXBUF_BG_CHRBANK_TBL + 0(t0)
	addiu	t2, t2, 1
	sh	t2, OFFSET_GFXBUF_BG_CHRBANK_TBL + 2(t0)
	addiu	t2, t2, 1
	sh	t2, OFFSET_GFXBUF_BG_CHRBANK_TBL + 4(t0)
	addiu	t2, t2, 1
	sh	t2, OFFSET_GFXBUF_BG_CHRBANK_TBL + 6(t0)
10:
	or	t3, t3, a1
	sb	t3, OFFSET_GFX_DRAW_BG_BANK(t4)

/* OBJ chr bank は変わったか ?
 * t1: PPU 2000H
 */
	li	t0, 0xff
/*	lbu	t2, OFFSET_OBJ_CHRBANK_CHANGE_COUNT(WORK) */
	lbu	t5, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	bne	t5, zero, 20f
	sb	t0, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)

#if 0
/* obj chr bank index 固定割り当てなので ここで OBJ chr seg を見ることはない */
	andi	t3, t1, 0x08  /* OBJ chr seg */
	sll	t2, t3, 1     /* 0x08 -> 0x10 */
	sb	t2, OFFSET_OBJ_CHRBANK_CHANGE_COUNT(WORK)
#endif

/* obj chr bank table をセットする */
	lw	t0, OFFSET_GFXPTR(WORK)
	li	t3, 0
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0x0(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0x2(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0x4(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0x6(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0x8(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0xa(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0xc(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0xe(t0)

20:
/* obj bank table entry は 0x00 固定。*/
	sb	zero, OFFSET_GFX_DRAW_OBJ_BANK(t4)

#if 0
/* debug */
	lw	a1, OFFSET_GFXPTR(WORK)
	subu	a1, t4, a1
	srl	a1, a1, 3
	PRINTF("line %x set done\n");
#endif
	j	mainloop_srlt0

	GLOBAL(chrbank_func03)
chrbank_func03:
/* CHR128/256
 * bank index は固定割り当て。
 * mapper 0x56 (moepro) でも使用。
 */
	lbu	t3, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK)
	lbu	t1, OFFSET_PPU2000H(WORK)
	bne	t5, zero, 10f
	sb	t0, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
/* chrbank change count は毎フレーム 0 に初期化されるので使えない。*/
	lhu	t3, OFFSET_CHRBANK_REG0000(WORK)
	sll	t3, t3, 1     /* 0x00/0x08/0x10/0x18 * 2 */
	
	andi	t2, t1, 0x10  /* BG chr seg */
	srl	t2, t2, 1     /* 0x10 -> 0x08 */
#if 0
/* debug */
	beq	t3, zero, 1f
	move a1,t3
	PRINTF("03_bg chrbank=%x\n");
1:
#endif
	or	t3, t3, t2
	sb	t3, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK)
/* bg chr bank table のセット */
	lw	t0, OFFSET_GFXPTR(WORK)
	addu	t0, t0, t3
	srl	t2, t3, 1     /* 0x08 -> 0x04 */
	sh	t2, OFFSET_GFXBUF_BG_CHRBANK_TBL + 0(t0)
	addiu	t2, t2, 1
	sh	t2, OFFSET_GFXBUF_BG_CHRBANK_TBL + 2(t0)
	addiu	t2, t2, 1
	sh	t2, OFFSET_GFXBUF_BG_CHRBANK_TBL + 4(t0)
	addiu	t2, t2, 1
	sh	t2, OFFSET_GFXBUF_BG_CHRBANK_TBL + 6(t0)
10:
	or	t3, t3, a1
#if 0
/* debug */
	lh	t0, OFFSET_LINECNT(WORK)
	bne	t0, zero, 1f
	move	a1, t3
	PRINTF("03_bg chrbank=%x\n");
1:
#endif
	sb	t3, OFFSET_GFX_DRAW_BG_BANK(t4)

/* OBJ chr bank は変わったか ?
 * t1: PPU 2000H
 */
	li	t0, 0xff
	lbu	t2, OFFSET_OBJ_CHRBANK_CHANGE_COUNT(WORK)
	lbu	t5, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	bne	t5, zero, 20f
	sb	t0, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)

/* chrbank change count は毎フレーム 0 に初期化されるので使えない。
 * obj chr bank index 固定割り当てなので ここで OBJ chr seg を見ることはない
 */
	lhu	t2, OFFSET_CHRBANK_REG0000(WORK)
	sll	t2, t2, 1
	sb	t2, OFFSET_OBJ_CHRBANK_CHANGE_COUNT(WORK)
/* obj chr bank table をセットする
 * 0x00/0x08/0x10/0x18 -> そのまま
 */
#if 0
/* debug */
	move	a1, t2
	PRINTF("03_obj chrbank=%x\n");
#endif
	lw	t0, OFFSET_GFXPTR(WORK)
	addu	t0, t0, t2
	srl	t3, t2, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0x0(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0x2(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0x4(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0x6(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0x8(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0xa(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0xc(t0)
	addiu	t3, t3, 1
	sh	t3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0xe(t0)
20:
	sb	t2, OFFSET_GFX_DRAW_OBJ_BANK(t4)
	j	mainloop_srlt0

	GLOBAL(chrbank_func04)
chrbank_func04:
/* MMC3
 * $1: pointer の chr seg
 */
	lbu	$1, OFFSET_MMC3_POINTER(WORK)
	andi	$1, $1, 0x80
	lbu	t3, OFFSET_DRAW_BG_CHRBANK_COUNT(WORK)
	lbu	t1, OFFSET_PPU2000H(WORK)
	bne	t5, zero, 10f

	sb	t0, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	srl	t0, $1, 3  /* 0x80 -> 0x10 */
	xor	t0, t0, t1
	andi	t0, t0, 0x10
/* t0 が 0 なら chr bank for 0000-,
 *       1 なら chr bank for 1000-, から。
 */
	srl	t0, t0, 1
	addu	t0, t0, WORK
	lw	a0, OFFSET_CHRBANK_REG0000(t0)
	lw	t6, OFFSET_CHRBANK_REG0800(t0)
	lw	t0, OFFSET_GFXPTR(WORK)
	li	t3, 0
	lbu	t2, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK)
/* a0/t6 と同じ値がすでにあれば、それ。
 * a2/a3: 比較対象
 * t3: draw chrbank count
 * t2: chrbank change count. 0 なら最初なので無条件登録、違えば t3 がこの値になる直前までループ
 * t0: gfxptr
 */
1:
	addu	t5, t0, t3
	beq	t2, t3, 2f  /* 同じものはなかった */
	lw	a2, OFFSET_GFXBUF_BG_CHRBANK_TBL + 0(t5)
	lw	a3, OFFSET_GFXBUF_BG_CHRBANK_TBL + 4(t5)

	xor	a2, a2, a0
	xor	a3, a3, t6
	or	a2, a2, a3
	beq	a2, zero, 3f  /* 同じ */
	
	sltiu	t5, t3, (BG_BANK_CHANGE_COUNT_MAX - 1) * 8
	addiu	t3, t3, 8
	bne	t5, zero, 1b
	addiu	t3, t3, -8
	j	3f  /* 切り替え回数オーバー */
2:
/*	addu	t5, t0, t3 いらんよな? */
	sw	a0, OFFSET_GFXBUF_BG_CHRBANK_TBL + 0(t5)
	sw	t6, OFFSET_GFXBUF_BG_CHRBANK_TBL + 4(t5)
	addiu	t2, t3, 8
	sb	t2, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK)
3:
	sb	t3, OFFSET_DRAW_BG_CHRBANK_COUNT(WORK)
	
10:
	or	t3, t3, a1
	sb	t3, OFFSET_GFX_DRAW_BG_BANK(t4)

/* OBJ chr bank は変わったか ?
 * t1: PPU 2000H
 */
	li	t0, 0xff
	lbu	t5, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	lbu	t3, OFFSET_DRAW_OBJ_CHRBANK_COUNT(WORK)
	bne	t5, zero, 20f
	sb	t0, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
/* pointer の chr seg が 0 のときは chr bank for 0000-, ..., 1000-, で、
 *                       1                       1000-, ..., 0000-, を。
 */
	srl	$1, $1, 4  /* 0x80 -> 0x08 */
	li	t3, 0
	lbu	t2, OFFSET_OBJ_CHRBANK_CHANGE_COUNT(WORK)
	lw	t0, OFFSET_GFXPTR(WORK)
/* {+0,+2,+4,...,+e} の bank の組は すでにあるものか ?
 * $1: 比較 src offset (0 or 8)
 * t3: draw chrbank count (0x10 bytes 単位)
 * t2: chrbank change count. 最初なら 0 なので無条件登録。
 *     違えば t3 がこの値になる直前までループ
 * t0: gfxptr
 * a0,a2,a3: 比較 work
 */
1:
	addu	t5, t0, t3
	beq	t2, t3, 2f  /* 同じものはなかった */
/* 比較 src,dst 読み込み */
	.set	noreorder
	addu	t6, $1, WORK
	lw	a2, OFFSET_CHRBANK_REG0000(t6)
	lw	a3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0(t5)
	xori	a0, $1, 0x08
	bne	a2, a3, 5f
	lw	a2, OFFSET_CHRBANK_REG0800(t6)
	lw	a3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 4(t5)
	addu	t6, a0, WORK
	bne	a2, a3, 5f
	lw	a2, OFFSET_CHRBANK_REG0000(t6)
	lw	a3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 8(t5)
	lw	a0, OFFSET_CHRBANK_REG0800(t6)
	bne	a2, a3, 5f
	lw	a3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0xc(t5)
	.set	reorder
	beq	a0, a3, 3f  /* 同じ */
5:
	sltiu	t5, t3, (OBJ_BANK_CHANGE_COUNT_MAX - 1) * 0x10
	addiu	t3, t3, 0x10
	bne	t5, zero, 1b
	addiu	t3, t3, -0x10
	j	3f  /* 切り替え回数オーバー */
2:
	addu	t6, $1, WORK
	lw	a2, OFFSET_CHRBANK_REG0000(t6)
	lw	a3, OFFSET_CHRBANK_REG0800(t6)
	sw	a2, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0(t5)
	sw	a3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 4(t5)
	
	xori	t6, $1, 0x08
	addu	t6, t6, WORK
	lw	a2, OFFSET_CHRBANK_REG0000(t6)
	lw	a3, OFFSET_CHRBANK_REG0800(t6)
	sw	a2, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 8(t5)
	sw	a3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0xc(t5)
	
	addiu	t2, t3, 0x10
	sb	t2, OFFSET_OBJ_CHRBANK_CHANGE_COUNT(WORK)
3:
	sb	t3, OFFSET_DRAW_OBJ_CHRBANK_COUNT(WORK)
20:
	sb	t3, OFFSET_GFX_DRAW_OBJ_BANK(t4)
	j	mainloop_srlt0

	GLOBAL(chrbank_func05)
chrbank_func05:
/* MMC5
 * debug preliminary
 */
	lbu	t3, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK)
	lw	$1, OFFSET_GFXPTR(WORK)
	bne	t5, zero, 10f
	sltiu	t5, t3, BG_BANK_CHANGE_COUNT_MAX * 8
	sb	t0, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	beq	t5, zero, 10f  /* 切り替え回数オーバー */
	
	lbu	t1, OFFSET_PPU2000H(WORK)
	addiu	t3, t3, 0x08
	sb	t3, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK)
	addu	t5, t3, $1
	andi	t2, t1, 0x20
	bne	t2, zero, 1f
/* obj 8x8 なので pbr0-3 or pbr4-7 を。
 */
	andi	t2, t1, 0x10
	srl	t2, t2, 1    /* 0x10 -> 0x08 */
	addu	t2, t2, WORK
	lw	t1, OFFSET_MMC5_PBR0 + 0(t2)
	lw	t2, OFFSET_MMC5_PBR0 + 4(t2)
	j	2f
1:
/* obj 8x16 mode なので pbr4'-pbr7' を 2 回。
 */
	lw	t1, OFFSET_MMC5_PBR0 + (8 + 0) * 2(WORK)
	lw	t2, OFFSET_MMC5_PBR0 + (8 + 2) * 2(WORK)
2:
	sw	t1, OFFSET_GFXBUF_BG_CHRBANK_TBL + 0(t5)
	sw	t2, OFFSET_GFXBUF_BG_CHRBANK_TBL + 4(t5)
10:
	or	t2, t3, a1
#if 0
/* debug */
	lhu	a1, OFFSET_LINECNT(WORK)
	move	a2, t3
	PRINTF("05_chrbank_func line=%x, count=%x\n");
#endif
	lbu	t5, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	lbu	t3, OFFSET_OBJ_CHRBANK_CHANGE_COUNT(WORK)
	sb	t2, OFFSET_GFX_DRAW_BG_BANK(t4)
	bne	t5, zero, 20f
	sltiu	t5, t3, OBJ_BANK_CHANGE_COUNT_MAX * 0x10
	sb	t0, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	beq	t5, zero, 20f  /* 切り替え回数オーバー */
	addiu	t3, t3, 0x10
	sb	t3, OFFSET_OBJ_CHRBANK_CHANGE_COUNT(WORK)
	addu	t5, t3, $1
	lw	t0, OFFSET_MMC5_PBR0 + 0(WORK)
	lw	t1, OFFSET_MMC5_PBR0 + 4(WORK)
	lw	t2, OFFSET_MMC5_PBR0 + 8(WORK)
	lw	a3, OFFSET_MMC5_PBR0 + 0xc(WORK)
	sw	t0, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0(t5)
	sw	t1, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 4(t5)
	sw	t2, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 8(t5)
	sw	a3, OFFSET_GFXBUF_OBJ_CHRBANK_TBL + 0xc(t5)
20:
	sb	t3, OFFSET_GFX_DRAW_OBJ_BANK(t4)
	j	mainloop_srlt0

	GLOBAL(chrbank_func44)
chrbank_func44:
/* Sunsoft4 - VROM
 * VROM モードなら
 *                 DRAW_VRAM_ADDR |= 0x40 を、
 *                 VROM_R   に VROM bank (right) を、
 * VROM bank (left) を D7-D0 とすると
 * DRAW 2000H の d7=D7, d6=D6,       d2-d0=D5-D3,
 * DRAW 2001H の d7-d5=D2-D0
 * に pack する。
 * NEXT_LINE を使おうと思ったけどうまく行かない。暴走する...
 * t4: gfxptr
 * t5: keep
 * a1: keep
 * t0: keep
 * 後は chrbank_func04 に任せる。
 */
	lbu	$1, OFFSET_SUN4_VROM_ENABLED(WORK)
	lbu	t1, OFFSET_GFX_DRAW_VRAM_ADDR(t4)
	beq	$1, zero, chrbank_func04
	lhu	a0, OFFSET_GFX_DRAW_2000H(t4)
/* t1: draw vram addr
 * t1 & 0x20 が、0 なら left には vrom0 を。
 *               0x20 なら        vrom1 を。
 * a2: left index (0 or 1)
 * t1 & 0x80 が 0 なら right には left と同じものを。
 *              0x80 なら         left と違うものを。
 * a3: right index (0 or 1)
 * t1 | 0x40 しておく。
 * なんか、aftrbrn2.nes の chrrom (0x40000 bytes) の、
 * 後半 0x20000 bytes の参照になっているようなので
 * chrbank no の d7 を 1 にしておく。
 */
	ori	t1, t1, 0x40
	sb	t1, OFFSET_GFX_DRAW_VRAM_ADDR(t4)
	andi	a2, t1, 0x20
	srl	a2, a2, 5     /* 0x20 -> 0x01 */
	andi	a3, t1, 0x80
	srl	a3, a3, 7     /* 0x80 -> 0x01 */
	xor	a3, a3, a2

	addu	a2, a2, WORK
	lbu	a2, OFFSET_SUN4_VROM0(a2)
	addu	a3, a3, WORK
	lbu	a3, OFFSET_SUN4_VROM1(a3)
	
	andi	a0, a0, 0x3800 | 0x001f
	ori	a2, a2, 0x80
	andi	t3, a2, 0xc0
	sll	t3, t3, 8      /* 0xc0 -> 0xc000 */
	or	a0, a0, t3
	andi	t3, a2, 0x3f
	sll	t3, t3, 1 + 4  /* 0x3f -> 0x07e0 */
	or	a0, a0, t3
	sh	a0, OFFSET_GFX_DRAW_2000H(t4)
	ori	a3, a3, 0x80
 	sb	a3, OFFSET_GFX_DRAW_VROM_R(t4)
	j	chrbank_func04
			

	
	GLOBAL(line_end)
line_end:
	lw	t2, OFFSET_SOUNDCYCLE(WORK)
	addiu	t2, t2, (-(LINECYCLE_CONST0 + LINECYCLE_CONST1) / 0x100)
	sw	t2, OFFSET_SOUNDCYCLE(WORK)
	lh	t1, OFFSET_LINECNT_IRQ(WORK)
	addiu	LINECYCLE, LINECYCLE, LINECYCLE_CONST0
	lh	t4, OFFSET_LINECNT(WORK)
	addiu	t4, t4, 1
	sh	t4, OFFSET_LINECNT(WORK)

	srl	t0, PC, 13
	bne	t1, t4, mainloop
/* ラインカウンタオーバー。
 * マッパーによって ラインカウンタオーバー関数へ。
 * その関数は mainloop_srlt0 へ帰る。
 * t4: new linecnt
 * t3: linecnt_irq preset 値
 * t2: linecnt_irq enabled
 */
	lw	t0, OFFSET_LINECNT_OVER_FUNCP(WORK)
	lhu	t3, OFFSET_LINECNT_IRQ_PRESET(WORK)
	lbu	t2, OFFSET_LINECNT_IRQ_ENABLED(WORK)
#if 0
/* debug */
	move	a1, t4
	move	a2, t3
	move	a3, t2
	PRINTF("linecnt over %x,%x,%x\n");
#endif
	jr	t0

	GLOBAL(vblank)
vblank:
/* linecnt == -21 なら vblank start, vblank begin
 * linecnt == -1 なら  vblank end
 * t4: linecnt
 */
	addiu	t2, t4, 1
	addiu	t1, t4, 21
	bne	t1, zero, 1f
/* vblank start
 * PPU 2002H output の d7 を 1 に 
 * NMI enabled なら activate
 * hudson/sforce.nes のスクロールブレ対策で、linecycle を調整する。-(0x22*0x100) ではブレる。
 *  -(0x24*0x100) だと devil.nes がブレる。
 * 今 -(0x28*0x100)
 */
	addiu	LINECYCLE, LINECYCLE, -(0x28 * 0x100)
	lb	t1, OFFSET_PPU2000H(WORK)  /* signed で読む */
	bgez	t1, mainloop_srlt0

	la	STORE_DONE, mainloop_srlt0
	li	t0, 0xfffa
	j	activate_intr

1:
	srl	t0, PC, 13
	bne	t2, zero, mainloop
/* vblank end
 * PPU 2002H output の d7,d6 を 0 に。
 * 2002H strikef position の計算。
 * draw sccv new -> draw sccv
 * oam -> draw oam へ。
 * palette -> draw palette
 * V ext mode 割り込みチェック
 */
#if 0
/* 2002H polling と NMI を併用しているものがある。(cloadrun.nes)
 * strikef も、 read reset なのかな ?
 */
	lbu	t1, OFFSET_PPU2000H_OUTPUT + 2(WORK)
	andi	t1, t1, 0x3f
	sb	t1, OFFSET_PPU2000H_OUTPUT + 2(WORK)
#endif
#if 0
/* line 240 done で行う */
/* 2002H strikef はクリアしよう (romf1.nes) */
	lbu	t1, OFFSET_PPU2000H_OUTPUT + 2(WORK)
	andi	t1, t1, 0xbf
	sb	t1, OFFSET_PPU2000H_OUTPUT + 2(WORK)
	li	t1, 0xff
	sb	t1, OFFSET_STRIKEF_LINE(WORK)
#endif
#if 0
/* debug
 * strikef 計算が雑。
 */
	lbu	t2, OFFSET_OAM(WORK)
	sb	t2, OFFSET_STRIKEF_LINE(WORK)
#endif
	
/* sccv = sccv new
 * PPU 2006H に何か書き込むとさあ、sccv new が変わって、
 * そのあと PPU 2005H に何も書かなければ スクロールはそのままなんだよね。
 * でもここでは強制的に SCCV にしちゃうでしょ、正しくないんじゃん ?
 */
	lbu	t0, OFFSET_DRAW_SCCV_NEW + 1(WORK)
	
	lbu	t1, OFFSET_DRAW_SCCV_NEW(WORK)
#if 0
/* debug
 * cluclu.nes と jaleco/ninjakun.nes の互換性は ?
 * これを入れると smario.nes の上がバグる。
 * なくても cluclu.nes, ninjakun.nes ちゃんと出る。
 */
	lbu	t1, OFFSET_PPU2000H(WORK)
#endif
	andi	t1, t1, 0x03
#if 0
/* debug
 * jaleco/ninjakun.nes はこれで動くけど
 * 他はやばいでしょう。こんなことしたら。
 * andi 0x03 でしょ? (cluclu.nes がバグる)
 */
	andi	t1, t1, 0x01
#endif
	sb	t1, OFFSET_DRAW_SCCV(WORK)
	sb	t0, OFFSET_DRAW_SCCV + 1(WORK)

#if 0
/* debug */
	lhu	a1, OFFSET_DRAW_SCCV(WORK)
	PRINTF("SCCV = SCCV_NEW = %x\n");
#endif
	
/* oam -> draw oam */
	lw	t0, OFFSET_GFXPTR(WORK)
	addiu	t1, t0, 0x100
	move	t2, WORK
1:
	lw	a0, OFFSET_OAM + 0(t2)
	lw	a1, OFFSET_OAM + 4(t2)
	lw	a2, OFFSET_OAM + 8(t2)
	lw	a3, OFFSET_OAM + 0xc(t2)

	sw	a0, OFFSET_GFXBUF_OAM0 + 0(t0)
	sw	a1, OFFSET_GFXBUF_OAM0 + 4(t0)
	sw	a2, OFFSET_GFXBUF_OAM0 + 8(t0)
	sw	a3, OFFSET_GFXBUF_OAM0 + 0xc(t0)
	
	addiu	t2, t2, 0x10
	addiu	t0, t0, 0x10
	bne	t0, t1, 1b
		
	ld	t2, OFFSET_PALETTE_BUF + 0x00(WORK)
	ld	t3, OFFSET_PALETTE_BUF + 0x08(WORK)
	ld	t4, OFFSET_PALETTE_BUF + 0x10(WORK)
	ld	t5, OFFSET_PALETTE_BUF + 0x18(WORK)
	lw	t0, OFFSET_GFXPTR(WORK)
	sd	t2, OFFSET_GFXBUF_PALETTE0 + 0x00(t0)
	sd	t3, OFFSET_GFXBUF_PALETTE0 + 0x08(t0)
	sd	t4, OFFSET_GFXBUF_PALETTE0 + 0x10(t0)
	sd	t5, OFFSET_GFXBUF_PALETTE0 + 0x18(t0)

#if 1
/* V int check (current code)
 * 4017H (d7,d6)=(0,0) なら V ext intr request.
 * cli なら割り込みをかける。
 */
	lbu	t0, OFFSET_WORK_4017H(WORK)
	lbu	t1, OFFSET_WORK_4015H_OUTPUT(WORK)
	bne	t0, zero, mainloop_srlt0

	lbu	t0, OFFSET_WORK_RBDI(WORK)
	andi	t2, t0, 0x04  /* I */
	ori	t1, t1, 0x40  /* V ext int req. */
	sb	t1, OFFSET_WORK_4015H_OUTPUT(WORK)
	
	lbu	t0, OFFSET_IRQ_CAUSE(WORK)
	andi	t0, t0, 0xff ^ 0x40
	sb	t0, OFFSET_IRQ_CAUSE(WORK)
	la	STORE_DONE, mainloop_srlt0
	beq	t2, zero, activate_intr_fffe
	
/* sei なら irq cause bit d6 を立てておく */
#if 0
	ori	t0, t0, 0x40
	sb	t0, OFFSET_IRQ_CAUSE(WORK)
#endif
#endif

	j	mainloop_srlt0


/* loadfunc のお約束
 * a0: emu-cpu addr (keep!)
 * v0: result (unsigned)
 * v1: keep!
 * ra: 下請け関数の return address
 * OFFSET_JUMPTBL+4(JUMPTBLP) のアドレスへ帰る。
 * PAGE_PTR: WORK + ((emu-cpu addr >> 13) << 2)
 * (keep!)   下請け関数のアドレスと、page table を参照するために使う
 * 
 * storefunc のお約束
 * a0: emu-cpu addr (keep!)
 * a1: data (d31-d8 が 0 でない場合もある)
 * a2: temp (cycle for サウンド)
 * v0, v1: 一応 keep!
 * STORE_DONE(t6): return address
 */
	GLOBAL(store_bbram_64k)
store_bbram_64k:
#if 0
/* debug */
	move	a2, a0
	PRINTF("BBRAM %02x->%x\n");
#endif
	addu	t0, a0, WORK
	sb	a1, OFFSET_BBRAM - 0x6000(t0)
	jr	STORE_DONE

/* v1 の値 (16bit) を push する。
 * v1 は破壊される。
 * t0: temp
 */
	GLOBAL(push_v1)
push_v1:
	addiu	t0, SP, -1
	andi	t0, t0, 0xff
	addu	t0, t0, WORK
	sb	v1, OFFSET_WRAM + 0x100(t0) /* low */
	srl	v1, v1, 8
	addu	t0, SP, WORK
	sb	v1, OFFSET_WRAM + 0x100(t0) /* high */
	addiu	SP, SP, -2
	andi	SP, SP, 0xff
	jr	ra

	GLOBAL(store_verbose)
store_verbose:
#ifdef KS_DEBUG_VERBOSE
/* debug */
	move	a3, PC
	move	a2, a1
	move	a1, a0
	PRINTF("%04x=%02x at PC=%04x\n");
#endif
	jr	STORE_DONE
	
	
/* store_done の delay slot を重ねてある */
	GLOBAL(store_done)
store_done:
store_ignore:
	.set	noreorder
	jr	STORE_DONE
	GLOBAL(store_internal_wram)
store_internal_wram:
	andi	t0, a0, 0x7ff
	.set	reorder
	addu	t0, t0, WORK
	sb	a1, OFFSET_WRAM(t0)
#if 0
/* debug */
	move a2,a1
	move a1,a0
	PRINTF("%x=%x\n");
#endif
	jr	STORE_DONE

/* load_done の delay slot を重ねてある */
	GLOBAL(load_done)
load_done:
	.set	noreorder
	jr	ra
	GLOBAL(load_internal_wram)
load_internal_wram:
/* 0000-1fff, address に mask が必要。*/
	andi	t0, a0, 0x7ff
	.set	reorder
	addu	t0, t0, WORK
	lbu	v0, OFFSET_WRAM(t0)
	jr	ra
	
	GLOBAL(load_6000)
load_6000:
/* 6000-7fff, 8000-ffff
 * PAGE_PTR を参照。
 */
	lw	t0, OFFSET_PAGE_TABLE(PAGE_PTR)
	addu	t0, t0, a0
	lbu	v0, 0(t0)
	jr	ra

	GLOBAL(load8_imm)
load8_imm: /* ロード後 func2 へ RMW 対応不要 */
	lw	ra, OFFSET_JUMPTBL + 4(JUMPTBLP)
	addiu a0,PC,-1; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jr t1

	GLOBAL(load16_imm)
load16_imm: /* high, low ロード後 func2 へ RMW 対応不要 jmp, jsr, sta, stx, sty で使う だけだろう。 jmp_6c, store 系 のことを考えて 結果は a0 に。*/
	addiu a0,PC,-1; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	sll	v1, v0, 8
	addiu a0,PC,-2; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	or	a0, v0, v1
	lw	ra, OFFSET_JUMPTBL + 4(JUMPTBLP)
	jr	ra

	GLOBAL(load8_zerop)
load8_zerop:
/* ロード後 func2 へ
 * RMW 対応必要なので a0 に v0 を、PAGE_PTR に WORK を入れる。
 * sta_91 でレジスタ依存しているので注意。
 */
	addiu a0,PC,-1; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	move	PAGE_PTR, WORK
	move	a0, v0
	addu	t0, a0, WORK
	lbu	v0, OFFSET_WRAM(t0)
	lw	ra, OFFSET_JUMPTBL + 4(JUMPTBLP)
	jr	ra

	GLOBAL(load8_abs)
load8_abs:
/* ロード後 func2 へ
 * RMW 対応必要。
 */
	addiu a0,PC,-1; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	sll	v1, v0, 8
	addiu a0,PC,-2; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	or	a0, v0, v1
			srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR);
	lw	ra, OFFSET_JUMPTBL + 4(JUMPTBLP)
	jr	t1

	GLOBAL(load8_absx)
load8_absx:
/* ロード後 func2 へ
 * RMW 対応必要。
 */
	addiu a0,PC,-1; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	sll	v1, v0, 8
	addiu a0,PC,-2; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	or	a0, v0, v1
	addu	a0, a0, X
			srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR);
	lw	ra, OFFSET_JUMPTBL + 4(JUMPTBLP)
	jr	t1

	GLOBAL(load8_dyi)
load8_dyi:
/* ロード後 func2 へ
 * (このアドレシングモードは RMW がない)
 */
	addiu a0,PC,-1; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	addu	t0, v0, WORK
	addiu	a0, v0, 1
	lbu	v0, OFFSET_WRAM(t0)
	andi	a0, a0, 0xff
	addu	t0, a0, WORK
	lbu	v1, OFFSET_WRAM(t0)
	addu	v0, v0, Y
	sll	v1, v1, 8
	addu	a0, v0, v1
			srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR);
	lw	ra, OFFSET_JUMPTBL + 4(JUMPTBLP)
	jr	t1

	GLOBAL(load8_dx)
load8_dx:
/* ロード後 func2 へ
 * RMW 対応必要なので a0 に (v0 + X) & 0xff を、PAGE_PTR に WORK を入れる。
 * sta_81 でレジスタ依存しているので注意。
 */
	addiu a0,PC,-1; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	move	PAGE_PTR, WORK
	addu	a0, v0, X
	andi	a0, a0, 0xff
	addu	t0, a0, WORK
	lbu	v0, OFFSET_WRAM(t0)
	lw	ra, OFFSET_JUMPTBL + 4(JUMPTBLP)
	jr	ra

/* load8_dy は ldx_b6 だけなので 割愛
 * ldx_a2 へ。
 */
ldx_b6: /* ldx z,y */
	addu	a0, v0, Y
	andi	a0, a0, 0xff
	addu	t0, a0, WORK
	lbu	v0, OFFSET_WRAM(t0)
	j	ldx_a2
		
	GLOBAL(load8_absy)
load8_absy:
/* ロード後 func2 へ
 * (このアドレシングモードは RMW がない)
 */
	addiu a0,PC,-1; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	sll	v1, v0, 8
	addiu a0,PC,-2; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	or	a0, v0, v1
	addu	a0, a0, Y
			srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR);
	lw	ra, OFFSET_JUMPTBL + 4(JUMPTBLP)
	jr	t1
	
	GLOBAL(load8_dxi)
load8_dxi:
/* ロード後 func2 へ
 * (このアドレシングモードは RMW がない)
 */
	addiu a0,PC,-1; srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	addu	a0, v0, X
	andi	a0, a0, 0xff
	addu	t0, a0, WORK
	lbu	v0, OFFSET_WRAM(t0)
	addiu	a0, a0, 1
	andi	a0, a0, 0xff
	addu	t0, a0, WORK
	lbu	v1, OFFSET_WRAM(t0)
	sll	v1, v1, 8
	or	a0, v0, v1
			srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR);
	lw	ra, OFFSET_JUMPTBL + 4(JUMPTBLP)
	jr	t1

/* func2 begins ... */
adc_61:
adc_65:
adc_69:
adc_6d:
adc_71:
adc_75:
adc_79:
adc_7d:
/* 符号拡張して足し、-128..+127 の範囲、それに +128 して 0..255 なら
 * オーバーフローなし
 * RMW ではないので j next; で帰る
 */
	addu	t0, A, v0
	addu	t0, t0, C  /* t0 = next carry 計算用 */
	
	sll	A, A, 24
	sra	A, A, 24
	addu	A, A, C

	sll	v0, v0, 24
	sra	v0, v0, 24
	addu	Z, A, v0
	andi	N, Z, 0x80

	addiu	V, Z, 128
	srl	V, V, 8  /* A+v0+C+128 が 0x100 以上なら V=1 */

	srl	C, t0, 8
	andi	A, t0, 0xff
	j	next

and_21:
and_25:
and_29:
and_2d:
and_31:
and_35:
and_39:
and_3d:
/*
 * RMW ではないので j next; で帰る
 */
	and	Z, A, v0
	andi	N, Z, 0x80
	andi	A, Z, 0xff
	j	next

asl_0a:
	sll	Z, A, 1
	andi	N, Z, 0x80
	srl	C, Z, 8
	andi	A, Z, 0xff
	j	next

asl_06:
asl_0e:
asl_16:
asl_1e:
/* RMW なので storefunc へ帰る */
	lw	t0, OFFSET_STOREFUNC_TBL(PAGE_PTR)
	sll	Z, v0, 1
	andi	N, Z, 0x80
	srl	C, Z, 8
	andi	a1, Z, 0xff
	jr	t0

#if 0
/* なんか サイクル数計算 めちゃくちゃちゃう ? */
bcc_90:	sll	t0, v0, 24;	bne	C, zero, next;	sra	t0, t0, 24;	addu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bcs_b0:	sll	t0, v0, 24;	beq	C, zero, next;	sra	t0, t0, 24;	addu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
beq_f0:	andi	t1, Z, 0xff;	sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	beq	t1, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bne_d0:	andi	t1, Z, 0xff;	sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	bne	t1, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bpl_10:	                        sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	beq	N, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bmi_30:	                      	sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	bne	N, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bvc_50:	sll	t0, v0, 24;	bne	V, zero, next;	sra	t0, t0, 24;	addu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bvs_70:	sll	t0, v0, 24;	beq	V, zero, next;	sra	t0, t0, 24;	addu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
#endif

bcc_90:				sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	beq	C, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bcs_b0:				sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	bne	C, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
beq_f0:	andi	t1, Z, 0xff;	sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	beq	t1, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bne_d0:	andi	t1, Z, 0xff;	sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	bne	t1, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bpl_10:	                        sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	beq	N, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bmi_30:	                      	sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	bne	N, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bvc_50:	                        sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	beq	V, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
bvs_70:	                        sll	t0, v0, 24;	sra	t0, t0, 24;	addu	PC, PC, t0;	bne	V, zero, next;
	subu	PC, PC, t0;	addiu	LINECYCLE, LINECYCLE, -0x100; j	next
	
bit_24:
bit_2c:
/* RMW ではないので j next; で帰る */
	and	Z, A, v0
	andi	N, v0, 0x80
	andi	V, v0, 0x40
	j	next

clc_18:
	li	C, 0
	j	next
	
cld_d8:
	lbu	t0, OFFSET_WORK_RBDI(WORK)
	andi	t0, t0, 0xff ^ 0x08
	sb	t0, OFFSET_WORK_RBDI(WORK)
	j	next
	
cli_58:
	lbu	t0, OFFSET_WORK_RBDI(WORK)
	andi	t1, t0, 0xff ^ 0x04
/* すでに 割り込み許可状態なら チェックしない */
	beq	t0, t1, next
/* t1 には 今の Reserved|B|D|I を。*/
#if 0
/* debug */
	move	a1, PC
	lhu	a2, OFFSET_LINECNT(WORK)
	PRINTF("CLI at PC=%x, line=%x\n");
#endif
	sb	t1, OFFSET_WORK_RBDI(WORK)  /* Reserved|B|D|I */
#if 0
/* check_intr_common は うまく動いていない。
 * 多重割り込み処理に失敗して暴走する (cv3j.nes)
 */
	j	check_intr_common
#endif
	j	next

clv_b8:
	li	V, 0
	j	next

cmp_c1:
cmp_c5:
cmp_c9:
cmp_cd:
cmp_d1:
cmp_d5:
cmp_d9:
cmp_dd:
/* RMW ではないので j next; で帰る
 * (A - M) >= 0 なら C = 1
 * unsigned で (A - M) < 256 なら C = 1
 */
	subu	Z, A, v0
	andi	N, Z, 0x80
	sltiu	C, Z, 0x100
	j	next
	
cpx_e0:
cpx_e4:
cpx_ec:
	subu	Z, X, v0
	andi	N, Z, 0x80
	sltiu	C, Z, 0x100
	j	next

cpy_c0:
cpy_c4:
cpy_cc:
	subu	Z, Y, v0
	andi	N, Z, 0x80
	sltiu	C, Z, 0x100
	j	next

dec_c6:
dec_ce:
dec_d6:
dec_de:
/* RMW なので storefunc へ帰る */
	lw	t0, OFFSET_STOREFUNC_TBL(PAGE_PTR)
	addiu	Z, v0, -1
	andi	N, Z, 0x80
	andi	a1, Z, 0xff
	jr	t0

dex_ca:
	addiu	Z, X, -1
	andi	N, Z, 0x80
	andi	X, Z, 0xff
	j	next
	
dey_88:
	addiu	Z, Y, -1
	andi	N, Z, 0x80
	andi	Y, Z, 0xff
	j	next

eor_41:
eor_45:
eor_49:
eor_4d:
eor_51:
eor_55:
eor_59:
eor_5d:
/* RMW ではない。*/
	xor	Z, A, v0
	andi	N, Z, 0x80
	andi	A, Z, 0xff
	j	next

inc_e6:
inc_ee:
inc_f6:
inc_fe:
/* RMW なので storefunc へ帰る */
	lw	t0, OFFSET_STOREFUNC_TBL(PAGE_PTR)
	addiu	Z, v0, 1
	andi	N, Z, 0x80
	andi	a1, Z, 0xff
	jr	t0

inx_e8:
	addiu	Z, X, 1
	andi	N, Z, 0x80
	andi	X, Z, 0xff
	j	next
	
iny_c8:
	addiu	Z, Y, 1
	andi	N, Z, 0x80
	andi	Y, Z, 0xff
	j	next

jmp_4c:
/* a0 に 16bit で operand が入っている。*/
	move	PC, a0
	j	next

jsr_20:
/* a0 に 16bit で operand が入っている。*/
	addiu	v1, PC, -1
	move	PC, a0
	move	ra, STORE_DONE  /* next */
	j	push_v1

rts_60:
	addiu	SP, SP, 2
	andi	SP, SP, 0xff
	addu	t0, SP, WORK
	lbu	PC, OFFSET_WRAM + 0x100(t0)
	sll	PC, PC, 8
	addiu	t0, SP, -1
	andi	t0, t0, 0xff
	addu	t0, t0, WORK
	lbu	t0, OFFSET_WRAM + 0x100(t0)
	or	PC, PC, t0
	addiu	PC, PC, 1
	andi	PC, PC, 0xffff
	jr	STORE_DONE  /* rti_40 から呼ばれるかも */
	
lda_a1:
lda_a5:
lda_a9:
lda_ad:
lda_b1:
lda_b5:
lda_b9:
lda_bd:
/* RMW ではない。*/
	move	Z, v0
	andi	N, Z, 0x80
	move	A, v0
	j	next

/* ldx_b6 からも ldx_a2 へやって来る。*/
ldx_a2:
ldx_a6:
ldx_ae:
ldx_be:
	move	Z, v0
	andi	N, Z, 0x80
	move	X, v0
	j	next

ldy_a0:
ldy_a4:
ldy_ac:
ldy_b4:
ldy_bc:
	move	Z, v0
	andi	N, Z, 0x80
	move	Y, v0
	j	next

lsr_4a:
	andi	C, A, 0x01
	srl	Z, A, 1
	andi	N, Z, 0x80
	andi	A, Z, 0xff
	j	next
	
lsr_46:
lsr_4e:
lsr_56:
lsr_5e:
/* RMW なので storefunc へ帰る */
	lw	t0, OFFSET_STOREFUNC_TBL(PAGE_PTR)
	andi	C, v0, 0x01
	srl	Z, v0, 1
	andi	N, Z, 0x80
	andi	a1, Z, 0xff
	jr	t0

ora_01:
ora_05:
ora_09:
ora_0d:
ora_11:
ora_15:
ora_19:
ora_1d:
/* RMW ではない。*/
	or	Z, A, v0
	andi	N, Z, 0x80
	andi	A, Z, 0xff
	j	next

rol_2a:
	sll	Z, A, 1
	or	Z, Z, C
	andi	N, Z, 0x80
	srl	C, Z, 8
	andi	A, Z, 0xff
	j	next
	
rol_26:
rol_2e:
rol_36:
rol_3e:
/* RMW なので storefunc へ帰る。*/
	lw	t0, OFFSET_STOREFUNC_TBL(PAGE_PTR)
	sll	Z, v0, 1
	or	Z, Z, C
	andi	N, Z, 0x80
	srl	C, Z, 8
	andi	a1, Z, 0xff
	jr	t0

ror_6a:
	sll	C, C, 8
	or	Z, A, C
	andi	C, A, 0x01
	srl	Z, Z, 1
	andi	N, Z, 0x80
	andi	A, Z, 0xff
	j	next

ror_66:
ror_6e:
ror_76:
ror_7e:
/* RMW なので storefunc へ帰る。*/
	lw	t0, OFFSET_STOREFUNC_TBL(PAGE_PTR)
	sll	C, C, 8
	or	Z, v0, C
	andi	C, v0, 0x01
	srl	Z, Z, 1
	andi	N, Z, 0x80
	andi	a1, Z, 0xff
	jr	t0

sbc_e1:
sbc_e5:
sbc_e9:
sbc_ed:
sbc_f1:
sbc_f5:
sbc_f9:
sbc_fd:
/* 符号拡張して引き、-128..+127 の範囲、それに +128 して 0..255 なら
 * オーバーフローなし
 * RMW ではないので j next; で帰る
 */

	xori	t0, C, 1    /* t0 = C ^ 1 */
	subu	t1, A, v0
	subu	t1, t1, t0  /* t1 = next carry 計算用 */

	sll	A, A, 24
	srl	A, A, 24
	subu	A, A, t0

	sll	v0, v0, 24
	sra	v0, v0, 24
	subu	Z, A, v0
	andi	N, Z, 0x80

	addiu	V, Z, 128
	srl	V, V, 8  /* A-C-v0+128 が 0x100 以上なら V=1 */

	sltiu	C, t1, 0x100   /* unsigned で t1 が 256 未満なら C=1 */
	andi	A, t1, 0xff
	j	next

sec_38:
	li	C, 1
	j	next

	GLOBAL(sed_f8)
sed_f8:
	lbu	t0, OFFSET_WORK_RBDI(WORK)
	ori	t0, t0, 0x08
	sb	t0, OFFSET_WORK_RBDI(WORK)
	j	next

	GLOBAL(sei_78)
sei_78:
	lbu	t0, OFFSET_WORK_RBDI(WORK)
	ori	t0, t0, 0x04
	sb	t0, OFFSET_WORK_RBDI(WORK)
	j	next

	GLOBAL(sta_85)
sta_85:
/* v0 に store するべき ゼロページ emu-cpu address が入っている。
 * 帰るのは j next; で良い。
 */
#if 0
/* debug
 * 2 or 3 を trap
 */
	andi	t1, v0, 0xfe
	xori	t1, t1, 2
	bne	t1, zero, 1f
	lw	t0, OFFSET_EMUFRAME(WORK)
	sltiu	t0, t0, 629
	bne	t0, zero, 1f
	move	a1, v0
	move	a2, A
	move	a3, PC
	PRINTF("%x=%x,%x\n");

#if 0
/* おまけに
 * PC=d5c1 なら 0x20 を or して逃げる。
 */
	xori	a3, a3, 0xd5c1
	bne	a3, zero, 1f
	lbu	a3, OFFSET_WRAM + 0x3(WORK)
	ori	a3, a3, 0x20
	sb	a3, OFFSET_WRAM + 0x3(WORK)
	j	next
#endif
	
1:
#endif
	addu	t0, v0, WORK
	sb	A, OFFSET_WRAM(t0)
	j	next

	GLOBAL(sta_91)
sta_91:
/* v0 に indirect するべき ゼロページ emu-cpu address が
 * load8_zerop によって読まれている。
 * a0 が残っているのでそれを利用。
 */
	addiu	a0, a0, 1
	andi	a0, a0, 0xff
	addu	t0, a0, WORK
	lbu	v1, OFFSET_WRAM(t0)
	addu	v0, v0, Y
	GLOBAL(sta_indirect_common)
sta_indirect_common:
	sll	v1, v1, 8
	addu	a0, v0, v1
	GLOBAL(sta_8d)
sta_8d: /* a0 に store するべき emu-cpu address が 16bit で入っている。*/
sta_indirect_common2:
	srl	t0, a0, 13
	sll	t0, t0, 2
	addu	PAGE_PTR, t0, WORK
	lw	t1, OFFSET_STOREFUNC_TBL(PAGE_PTR)
	move	a1, A
	jr	t1

	GLOBAL(sta_95)	
sta_95:
/* v0 に ゼロページ emu-cpu address が入っている。
 * これに X を加えて andi 0xff した emu-cpu address へ store.
 * 帰るのは j next; で良い。
 */
	addu	a0, v0, X
	andi	a0, a0, 0xff
	addu	t0, a0, WORK
	sb	A, OFFSET_WRAM(t0)
	j	next

	GLOBAL(sta_81)
sta_81:
/* v0 に indirect するべき emu-cpu address L が
 * load8_dx によって読まれている。
 * a0 が残っているのでそれを利用。
 */
	addiu	a0, a0, 1
	andi	a0, a0, 0xff
	addu	t0, a0, WORK
	lbu	v1, OFFSET_WRAM(t0)
	j	sta_indirect_common
	
sta_99:
/* a0 に emu-cpu address が 16bit で入っている。
 * これに Y を加えたものが store するべき emu-cpu address.
 */
	addu	a0, a0, Y
#if 0
/* debug */
	xori	t0, a0, 0x28a
	bne	t0, zero, 1f
	PRINTF("028a=%x\n");
1:
#endif
	j	sta_indirect_common2
	
sta_9d:
	addu	a0, a0, X
#if 0
/* debug */
	move	a2, a0
	PRINTF("sta_9d watch %x,%x\n");
	teq	a2, 0x6060
#endif

#if 0
/* debug */
	xori	t0, a0, 0x67e
	bne	t0, zero, 1f
	lw	t0, OFFSET_EMUFRAME(WORK)
	sltiu	t0, t0, 600
	bne	t0, zero, 1f
	move	a2, PC
	PRINTF("67e=%x at PC=%x\n");
	sw	PC, OFFSET_TRACE_FLAG(WORK)
1:
#endif
	
	j	sta_indirect_common2

stx_86:
/* v0 に store するべき ゼロページ emu-cpu address が入っている。
 * 帰るのは j next; で良い。
 */
	addu	t0, v0, WORK
	sb	X, OFFSET_WRAM(t0)
	j	next

stx_8e:
/* a0 に store するべき emu-cpu address が 16bit で入っている。*/
	srl	t0, a0, 13
	sll	t0, t0, 2
	addu	PAGE_PTR, t0, WORK
	lw	t1, OFFSET_STOREFUNC_TBL(PAGE_PTR)
	move	a1, X
	jr	t1

stx_96:
/* v0 に ゼロページ emu-cpu address が入っている。
 * これに Y を加えて andi 0xff した emu-cpu address へ store.
 * 帰るのは j next; で良い。
 */
	addu	a0, v0, Y
	andi	a0, a0, 0xff
	addu	t0, a0, WORK
	sb	X, OFFSET_WRAM(t0)
	j	next

sty_84:
/* v0 に store するべき ゼロページ emu-cpu address が入っている。
 * 帰るのは j next; で良い。
 */
	addu	t0, v0, WORK
	sb	Y, OFFSET_WRAM(t0)
	j	next

sty_8c:
/* a0 に store するべき emu-cpu address が 16bit で入っている。*/
	srl	t0, a0, 13
	sll	t0, t0, 2
	addu	PAGE_PTR, t0, WORK
	lw	t1, OFFSET_STOREFUNC_TBL(PAGE_PTR)
	move	a1, Y
	jr	t1

sty_94:
/* v0 に ゼロページ emu-cpu address が入っている。
 * これに X を加えて andi 0xff した emu-cpu address へ store.
 * 帰るのは j next; で良い。
 */
	addu	a0, v0, X
	andi	a0, a0, 0xff
	addu	t0, a0, WORK
	sb	Y, OFFSET_WRAM(t0)
	j	next

tax_aa:
	move	Z, A
	andi	N, Z, 0x80
	move	X, A
	j	next
	
tay_a8:
	move	Z, A
	andi	N, Z, 0x80
	move	Y, A
	j	next
	
tsx_ba:
	move	Z, SP
	andi	N, Z, 0x80
	move	X, SP
	j	next
	
txa_8a:
	move	Z, X
	andi	N, Z, 0x80
	move	A, X
	j	next
	
txs_9a:
	move	SP, X
	j	next
	
tya_98:
	move	Z, Y
	andi	N, Z, 0x80
	move	A, Y
	j	next

pha_48:
	addu	t0, SP, WORK
	sb	A, OFFSET_WRAM + 0x100(t0)
	addiu	SP, SP, -1
	andi	SP, SP, 0xff
	j	next

pla_68:
	addiu	SP, SP, 1
	andi	SP, SP, 0xff
	addu	t0, SP, WORK
	lbu	A, OFFSET_WRAM + 0x100(t0)
	move	Z, A
	andi	N, Z, 0x80
	j	next

brk_00:
/* PC, P を push して、(t0) の emu-cpu addr へ PC をセット。
 * B フラグを立てるということだが、実機で立ってないような ...?
 * それなら thru じゃん?
 */
	
	GLOBAL(activate_intr_fffe)
activate_intr_fffe:
	li	t0, 0xfffe
/* thru */
	GLOBAL(activate_intr)
activate_intr:
/* PC, P を push して、(t0) の emu-cpu addr へ PC をセット。
 * STORE_DONE へ帰る。
 * plp_28 からも呼ばれる。
 * t4 壊さないでね (ここを呼んでいる元の関係で)
 */
	move	v1, PC
	lw	t1, OFFSET_PAGE_TABLE + 0x1c(WORK)
	addu	t1, t1, t0
	lbu	t2, 1(t1)  /* new PC high */
	lbu	t1, 0(t1)  /* new PC low  */
	sll	t2, t2, 8
	or	PC, t1, t2
#if 0
/* debug */
	move	a1, t0
	move	a2, PC
	lhu	a3, OFFSET_LINECNT(WORK)
	PRINTF("intr(%x) newPC=%x at line=%x\n");
#endif
#if 0
/* gradius2.nes がブレるのでここではやらない */
	addiu	LINECYCLE, LINECYCLE, 6 * 0x100
#endif
	jal	push_v1
	li	t2, 0x04
/* thru!! */
	GLOBAL(php_common)
php_common:
/* php_08 からも呼ばれる。
 * t2: フラグ push 後 SEI するなら 4, しないなら 0
 */
	lbu	t0, OFFSET_WORK_RBDI(WORK)
	or	t2, t2, t0
	sb	t2, OFFSET_WORK_RBDI(WORK)
	andi	t1, Z, 0xff
	sltiu	t1, t1, 1
	sll	t1, t1, 1     /* Z */
	or	t1, t1, N     /* N|Z */
	or	t1, t1, C     /* N|Z|C */
	or	t1, t1, t0    /* N|-|Reserved|B|D|I|Z|C */
	beq	V, zero, 1f
	ori	t1, t1, 0x40  /* N|V|Reserved|B|D|I|Z|C */
1:
	addu	t0, SP, WORK
	sb	t1, OFFSET_WRAM + 0x100(t0)
	addiu	SP, SP, -1
	andi	SP, SP, 0xff
	jr	STORE_DONE  /* j next; ではダメ */

	GLOBAL(php_08)
php_08:
	li	t2, 0
	j	php_common
	
plp_28:
/* N=1 かつ Z=1 というのは ありえる話で、
 * NZ では対応できない。
 * でも、そんなこと しないよね...
 * してた。bit, exbike.nes
 * 割り込み復帰処理の最初にも呼ばれるので j next; で帰っちゃダメ。
 */
	addiu	SP, SP, 1
	andi	SP, SP, 0xff
	addu	t0, SP, WORK
	lbu	t0, OFFSET_WRAM + 0x100(t0)
	andi	t1, t0, 0x3c
	lbu	t3, OFFSET_WORK_RBDI(WORK)  /* Reserved|B|D|I */
	sb	t1, OFFSET_WORK_RBDI(WORK)  /* Reserved|B|D|I */
	andi	C, t0, 0x01                 /* C */
	andi	V, t0, 0x40                 /* V */
	andi	Z, t0, 0x02
	xori	Z, Z, 0x02                  /* Z */
	andi	N, t0, 0x80                 /* N */
/* I が 1->0 になったら 割り込みチェックする */
	xor	t4, t3, t1
	and	t4, t4, t3
	andi	t4, t4, 0x04
	beq	t4, zero, store_done
	
	GLOBAL(check_intr_common)
check_intr_common:
	jr	STORE_DONE
#if 0
/* うまく機能していない。
 * 多重割り込み処理に失敗して暴走する (cv3j.nes)
 */
/* 割り込みチェック
 * irq cause が 0 でなければ hit, 該当 bit を下ろす
 * cli_58 からも来る。
 */
	lbu	t0, OFFSET_IRQ_CAUSE(WORK)
	li	t1, 0x80
	beq	t0, zero, store_done
1:
	and	t2, t0, t1
	srl	t1, t1, 1
	beq	t2, zero, 1b

	xor	t0, t0, t2
	sb	t0, OFFSET_IRQ_CAUSE(WORK)
	j	activate_intr_fffe  /* STORE_DONE へ帰る */
#endif
	
	
jmp_6c:
/* a1 の emu-cpu addr から 2bytes を 読む。
 * 読んだ瞬間に割り込みがかかるような I/O port はないから PC は つぶしても良い。
 */
			srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	addiu	a0, a0, 1
	move	PC, v0
			srl t0,a0,13; sll t0,t0,2; addu PAGE_PTR,t0,WORK; lw t1,OFFSET_LOADFUNC_TBL(PAGE_PTR); jalr t1
	sll	v0, v0, 8
	or	PC, PC, v0
	j	next

rti_40:
/* P, PC の復帰。
 */
	la	STORE_DONE, 1f
	j	plp_28
1:
	la	STORE_DONE, 1f
	j	rts_60
1:
	addiu	PC, PC, -1
	andi	PC, PC, 0xffff
	j	next
			
	GLOBAL(load_ppu)
load_ppu:
/* 2002 なら read reset. debug あと、表示ラインもずれるよね ...
 * 2007 なら 処理多い。
 */
	andi	t0, a0, 0x07
	xori	t1, t0, 0x07
	addu	t0, t0, WORK
	beq	t1, zero, 1f
	lbu	v0, OFFSET_PPU2000H_OUTPUT(t0)
#if 0
/* debug */
	move	a1, a0
	PRINTF("%04xR\n");
#endif
	li	t2, 0x20
	sb	t2, OFFSET_PPU2000H_OUTPUT(t0) /* 2002H read reset */
	jr	ra
1:
#if 0
/* debug */
	move	a1, PC
	lhu	a2, OFFSET_LINECNT(WORK)
	PRINTF("2007R at PC=%x,line=%x");
#endif

/* debug
 * 表示期間中の読み出しなら draw sccv ++;
 * どうせ 縦の 境界越えはないよね。
 */
	lh	t2, OFFSET_LINECNT(WORK)
	lhu	t3, OFFSET_DRAW_SCCV(WORK)
	bltz	t2, 7f

	andi	$1, t3, 0x300
	addiu	t3, t3, 1
	andi	t3, t3, 0xff
	xori	t2, t3, 0xf0
	bne	t2, zero, 1f
	xori	$1, $1, 0x200
	li	t3, 0
1:
	or	t3, t3, $1
	sh	t3, OFFSET_DRAW_SCCV(WORK)
	
7:
/* 2007 read mode が 0 なら 0 でなくして 空読み。
 */
	lhu	t0, OFFSET_PPU_ADDR(WORK)
	lbu	t1, OFFSET_PPU2000H + 7(WORK) /* 2007 read mode として使用 */
	bne	t1, zero, 1f
2:
/* 空読み/読み出し無効
 * read mode を 1 に。
 * a0 の下位は明らかに 0 ではない。
 */
	li	v0, 0
	sb	a0, OFFSET_PPU2000H + 7(WORK) /* 2007 read mode として使用 */
	jr	ra
1:
/* ppu_addr < 0x2000 なら chrrom/chrram
 *          < 0x3000 なら vram
 * 他は 無視
 */
	sltiu	t1, t0, 0x2000
	sltiu	t4, t0, 0x3000
	bne	t1, zero, 1f    /* chrrom/chrram */
	
	lhu	t2, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	lhu	t3, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	beq	t4, zero, load_2007_done
/* vram */
	and	t2, t2, t0
	or	t2, t2, t3
	srl	t3, t2, 1
	or	t2, t2, t3

	andi	t2, t2, 0x400
	andi	t0, t0, 0x3ff
	or	t0, t0, t2
	addu	t0, t0, WORK
	lbu	v0, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM(t0)
#if 0
/* debug */
	move	a1, v0
	PRINTF("val=%x\n");
#endif
	
load_2007_done:
	move	STORE_DONE, ra
	j	store_2007_done  /* 書き込みじゃないけど */

1:
/* chrrom/chrram
 * t0: ppu_addr
 * 読み出し時にも MMC3 pointer の chr seg が関係する。(motherj.nes, chrrom にプログラム)
 */
	lhu	t1, OFFSET_CHRSEG_FOR_READ(WORK)
	andi	t2, t0, 0x1c00
	xor	t2, t2, t1
	srl	t2, t2, 9      /* (/ 0x400) * 2 */
	addu	t2, t2, WORK
	lhu	t2, OFFSET_CHRBANK_REG0000(t2)
#if 0
/* debug */
	move	a1, t0
	move	a2, t2
	PRINTF("chrbank for %x=%x, ");
#endif
	lw	t1, OFFSET_CHRROM(WORK)
	andi	t3, t0, 0x03ff
	mul	t2, t2, 0x400
	addu	t2, t2, t3
	addu	t1, t1, t2
	lbu	v0, 0(t1)
#if 0
/* debug */
	move	a1, v0
	PRINTF("value=%x\n");
#endif
	j	load_2007_done


	GLOBAL(loade1_io)
loade1_io:
/* 110 in 1
 */
	addiu	t0, a0, -0x5800
	andi	t1, a0, 0x0003
	bltz	t0, load_io
	addu	t1, t1, WORK
	lbu	v0, OFFSET_BBRAM(t1)
	jr	ra
		
	GLOBAL(load_io)
load_io:
/* 4000-5fff
 * 4000-4013, 4015: sound
 * 4016: joypad
 * 4017: V ext mode
 * 4097 までは disksys サウンド関係がある。
 * 4030-4033: QD
 */
	andi	t0, a0, 0x1fff
	sltiu	t1, a0, 0x4018
	beq	t1, zero, 1f
	sll	t0, t0, 2
	addu	t0, t0, WORK
	lw	t0, OFFSET_LOAD_IO_FUNCTBL(t0)
	.set	noreorder
	jr	t0
1:
/* 4018-402f は invalid
 * 上の jr t0 の delay slot に入っても かまわない。
 */
	addiu	t1, a0, -0x4030
	.set	reorder
	addiu	t2, a0, -0x4034
	bltz	t1, load_invalid
/* a0=4030 -> t2=-4 -> t1=0
 *    4031       -3       1
 *    4032       -2       2
 *    4033       -1       3
 */
	addu	t3, t1, WORK
	bgez	t2, load_sound
	lbu	v0, OFFSET_QD4030H(t3)
	bne	t1, zero, 1f
/* 4030 read reset */
	sb	zero, OFFSET_QD4030H(WORK)
	jr	ra
1:
	li	t0, 0x4031
	lhu	t2, OFFSET_QDOFFSET + 2(WORK)
	bne	t0, a0, load_done
	addiu	t2, t2, 1
	sh	t2, OFFSET_QDOFFSET + 2(WORK)
	jr	ra

	GLOBAL(store_ppu)
store_ppu:
/* 2001, 2003 は書くだけ
 * t0 に a0 & 7 (keep!)
 * t1 に t0 + WORK
 * t3 に latch order (d0 のみ使う)
 * reorder がバカなので手で。
 */
	andi	t0, a0, 0x07
	sll	t1, t0, 2
	addu	t1, t1, WORK
	lw	t2, OFFSET_STORE_PPU_FUNCTBL(t1)
	lbu	t3, OFFSET_PPU2000H_OUTPUT + 2(WORK)
	.set	noreorder
	jr	t2
	addu	t1, t0, WORK
	.set	reorder
	
	GLOBAL(store_ppu_ram)
store_ppu_ram:
/* t0 に a0 & 7
 * t1 に t0 + WORK
 */
#if 0
/* debug */
	move	a2, a0
	lhu	a3, OFFSET_LINECNT(WORK)
	PRINTF("PPU_RAM(%x->%x)line=%x\n");
1:
#endif
	
	sb	a1, OFFSET_PPU2000H(t1)
	jr	STORE_DONE

	GLOBAL(store_2005)
store_2005:
/* latch order により draw scroll counter を変更する。
 * 奇数回目 - DRAW_SCCH を変更。すぐ有効になる。
 * 偶数回目 - DRAW_SCCV_NEW (low) を変更。これは次のフレームから 有効になる。
 * t3 に latch order
 */
	xori	t3, t3, 1
	andi	t1, t3, 1
	sb	t3, OFFSET_PPU2000H_OUTPUT + 2(WORK)
	beq	t1, zero, 1f
/* 奇数回目 */
#if 0
/* debug */
	lhu	a3, OFFSET_SERIAL4016D0(WORK)
	andi	a3, a3, 0x2000
	beq	a3, zero, 20f
	lhu	a2, OFFSET_LINECNT(WORK)
	PRINTF("2005H scch=%x at line=%x\n");
20:
#endif
	sb	a1, OFFSET_DRAW_SCCH(WORK)
	jr	STORE_DONE
1:
/* 偶数回目 */
#if 0
/* debug */
	lhu	a3, OFFSET_SERIAL4016D0(WORK)
	andi	a3, a3, 0x2000
	beq	a3, zero, 20f
	lhu	a2, OFFSET_LINECNT(WORK)
	PRINTF("2005H sccv new=%x at line=%x\n");
20:
#endif
	sb	a1, OFFSET_DRAW_SCCV_NEW + 1(WORK)
	jr	STORE_DONE

	GLOBAL(store_2006)
store_2006:
/* latch order により
 * 奇数回目 - ppu_addr high を変更。
 * 偶数回目 - ppu_addr low を変更。
 * t3 に latch order
 * a2: ppu_addr high
 * a1: ppu_addr low  にする
 * 2007 read mode を 0 にする
 */
	sb	zero, OFFSET_PPU2000H + 7(WORK) /* 2007 read mode として使用 */
	xori	t3, t3, 1
	andi	t1, t3, 1
	sb	t3, OFFSET_PPU2000H_OUTPUT + 2(WORK)
	beq	t1, zero, 1f
/* 奇数回目 */
	andi	t0, a1, 0x3f
	lbu	a1, OFFSET_PPU_ADDR + 1(WORK)
	sb	t0, OFFSET_PPU_ADDR + 0(WORK)
	move	a2, t0
	j	2f
1:
	lbu	a2, OFFSET_PPU_ADDR + 0(WORK)
	sb	a1, OFFSET_PPU_ADDR + 1(WORK)
2:
#if 0
/* debug */
	lhu	a3, OFFSET_SERIAL4016D0(WORK)
	andi	a3, a3, 0x2000
	beq	a3, zero, 20f
	lhu	a3, OFFSET_LINECNT(WORK)
	PRINTF("ppu_addr L,H=%02x,%02x at line=%x\n");
20:
#endif
/* devil.nes は、old code でないとバグる。
 */
#ifdef CODE_DEVIL
/* old code */
/* draw scroll counter を変更する。
 * 変換式:
 * ppu_addr = 00YYyyyy yyyXXXXX
 * draw scch = XXXXX * 8
 * draw sccv new (high, low) = (yyyy yyy) * 8 + YY
 * ... だと思うけど、 low は無視。
 * draw sccv にも即座に反映させる。
 * a2: ppu_addr high
 * a1: ppu_addr low
 */
	sll	t0, a1, 3
	sb	t0, OFFSET_DRAW_SCCH(WORK)
	srl	t0, a2, 4
	andi	t0, t0, 0x03  /* YY */
	andi	t1, a2, 0x0f
	sll	t1, t1, 3 + 3 /* yyyy yyy の yyyy */
	srl	t2, a1, 5 - 3 /* yyyy yyy の yyy  */
	andi	t2, t2, 0x38
	or	t0, t0, t1
	or	t0, t0, t2
	sh	t0, OFFSET_DRAW_SCCV(WORK)
#endif

/* devil.nes は、old code でないとバグる。
 */
#ifndef CODE_DEVIL
/* new code */
/* draw scroll counter を変更する。
 * 変換式:
 * ppu_addr = 00YYyyyy yyyZZZ00
 * draw scch = 0
 * draw sccv new (high, low) = (yyyy yyy) * 8 + YY + ZZZ
 * cv3j.nes の縦スクロールのとき
 * 28c0,28bc,28b8,28b4,28b0,28ac とかしてる。
 * draw sccv にも即座に反映させる。
 * a2: ppu_addr high
 * a1: ppu_addr low
 */
	sb	zero, OFFSET_DRAW_SCCH(WORK)
	srl	t0, a2, 4
	andi	t0, t0, 0x03  /* YY */
	andi	t1, a2, 0x0f
	sll	t1, t1, 3 + 3 /* yyyy yyy の yyyy */
	srl	t2, a1, 5 - 3 /* yyyy yyy の yyy  */
	andi	t2, t2, 0x38
	or	t0, t0, t1
	or	t0, t0, t2
	andi	t1, a1, 0x1c
	srl	t1, t1, 2
	addu	t0, t0, t1
	andi	t1, t0, 0xff
	sltiu	t1, t1, 0xf0
	bne	t1, zero, 1f
	andi	t1, t0, 0x200
	addiu	t0, t0, 0x10
	andi	t0, t0, 0xff
	or	t0, t0, t1
1:
	sh	t0, OFFSET_DRAW_SCCV(WORK)
#endif
	
/* debug
 * cluclu.nes, jaleco/ninjakun.nes 実験
 */
	sh	t0, OFFSET_DRAW_SCCV_NEW(WORK)

	jr	STORE_DONE


	GLOBAL(store_palette)
store_palette:
/* t0: ppu_addr
 * t2 が 1 なら ppu_addr < 0x3f00 なので無効。
 *
 */
	sb	zero, OFFSET_PPU2000H + 7(WORK) /* 2007 read mode として使用 */
	andi	t1, t0, 0x1f
	bne	t2, zero, store_2007_done
/* パレット
 * 3f00 or 3f10 なら background color
 */
	andi	t2, t1, 0x0f
	addu	t3, t1, WORK
	beq	t2, zero, 1f
/* 3f04,08,0c, 14,18,1c は無視 */
	andi	t2, t1, 0x03
	beq	t2, zero, store_2007_done
	andi	t0, a1, 0x3f
	sb	t0, OFFSET_PALETTE_BUF(t3)
	j	store_2007_done
1:
	andi	t0, a1, 0x3f
	sb	t0, OFFSET_PALETTE_BUF + 0x00(WORK)
	sb	t0, OFFSET_PALETTE_BUF + 0x04(WORK)
	sb	t0, OFFSET_PALETTE_BUF + 0x08(WORK)
	sb	t0, OFFSET_PALETTE_BUF + 0x0c(WORK)
	sb	t0, OFFSET_PALETTE_BUF + 0x10(WORK)
	sb	t0, OFFSET_PALETTE_BUF + 0x14(WORK)
	sb	t0, OFFSET_PALETTE_BUF + 0x18(WORK)
	sb	t0, OFFSET_PALETTE_BUF + 0x1c(WORK)
	j	store_2007_done

	GLOBAL(store_2007_chrram)
store_2007_chrram:
	lhu	t0, OFFSET_PPU_ADDR(WORK)
	sltiu	t1, t0, 0x2000
	sltiu	t3, t0, 0x3000
	beq	t1, zero, 7f
/* chrram なので、original format と internal format をセット。
 * bank は ... 無視できない(goemon1j.nes)。
 * とりあえず chrbank @ 0000- の レジスタだけ 見よう。
 */
	lhu	t4, OFFSET_CHRBANK_REG0000(WORK)
	mul	t4, t4, 0x400
	lw	t2, OFFSET_CHRROM(WORK)
	addu	t2, t2, t0
	addu	t2, t2, t4
	sb	a1, 0(t2)
#if 0
/* debug */
	la	t1, debug_ptr
	lw	t3, 0(t1)
	lw	t1, 0(t3)
	tge	t1,zero
#endif
/* (ppu_addr & 0x07) -> dst. offset +0,+0x80,+0x100,...,+0x380
 * (ppu_addr & 0x3f0) -> dst. offset +0,+2,+4,...,+0x7e
 * (ppu_addr & 0x08) -> dst.offset +1,+0
 */
	andi	t1, t0, 0x07
	sll	t1, t1, 7    /* 0x01 -> 0x80 */
	andi	t2, t0, 0x3f0
	srl	t2, t2, 3    /* 0x3f0 -> 0x7e */
	addu	t1, t1, t2
	andi	t2, t0, 0x1c00
	addu	t1, t1, t2
	andi	t2, t0, 0x08
	srl	t2, t2, 3    /* 0x08 -> 0x01 */
	xori	t2, t2, 1
	addu	t1, t1, t2
	addu	t1, t1, WORK
	addu	t1, t1, t4
	sb	a1, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_CHR(t1)

#if 0
/* debug */
	lw	a2, OFFSET_EMUFRAME(WORK)
	sltiu	a2, a2, 20
	bne	a2, zero, 10f
	xori	a2, a1, 0x70
	bne	a2, zero, 10f
	sw	a1,OFFSET_TRACE_FLAG(WORK)
10:
#endif
	j	store_2007_done
	
	GLOBAL(store_2007_chrrom)
store_2007_chrrom:
/*
 * ppu_addr < 0x2000 なら 無視。 (debug chrram 未対応)
 * ppu_addr > 0x2fff なら palette かも。
 * t0: ppu_addr
 */
	lhu	t0, OFFSET_PPU_ADDR(WORK)
	sltiu	t1, t0, 0x2000
	sltiu	t3, t0, 0x3000
	bne	t1, zero, store_2007_done
7: /* store_2007_chrram からも来る。*/
	sltiu	t2, t0, 0x3f00
	beq	t3, zero, store_palette
	GLOBAL(store_vram)
store_vram:
#if 0
/* debug */
	move	a2, t0
	PRINTF("%x->vram[%x]\n");
#endif
/* t0: ppu_addr
 * mirror mask and は 0x000 -> one screen mode
 *                    0x400 -> 横持ち
 *                    0x800 -> 縦持ち
 *                    0xc00 -> ありえない
 * mirror mask or  は 0x000 -> 通常モード or one screen mode @ vram[0x000]
 *                    0x400 -> one screen mode @ vram[0x400]
 *                    0x800 -> one screen mode @ vram[0x800] (未実装)
 *                    0xc00 -> ありえない
 * ppu_addr に mask and して、 mask or したものの 0xc00 の位は
 * (0xc00 は未実装なので除く)
 * 0 or 0x400 or 0x800 になる。
 * 0x400 or 0x800 なら vram[0x400] からにしたいので
 * >> 1 したものと or した 0x400 の位 を採用する。
 */
	lhu	t2, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	lhu	t3, OFFSET_VRAM_MIRROR_MASK_OR(WORK)

	and	t2, t2, t0
	or	t2, t2, t3
	srl	t3, t2, 1
	or	t2, t2, t3

	andi	t2, t2, 0x400
	andi	t3, t0, 0x3ff
	or	t2, t2, t3
#if 0
/* debug */
	xori	a2, t2, 0x480
	bne	a2, zero, 1f
	xori	a2, a1, 0x88
	bne	a2, zero, 1f
	sw	a1, OFFSET_TRACE_FLAG(WORK)
1:
#endif
	addu	t1, t2, WORK
	sb	a1, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM(t1)
/*
 * attr なら 該当 draw vram attr を変更する。
 * t0: ppu_addr
 * t2: vram mirror を考慮した ppu_addr
 * t3: (ppu_addr & 0x3ff) -0x3c0
 */
	addiu	t3, t3, -0x3c0
	bltz	t3, store_2007_done
/* ppu_addr の下位 0x3c0-0x3ff について
 * 23c0-23c7: 2000-207f
 * 23c8-23cf: 2080-20ff
 * 23d0-23d7: 2100-217f
 * 23d8-23df: 2180-21ff
 * 23e0-23e7: 2200-227f
 * 23e8-23ef: 2280-22ff
 * 23f0-23f7: 2300-237f
 * 23f8-23ff: 2380-23ff
 * の attr が変わる。
 * つまり ppu_addr のさらに下位 0x3f について
 * 00: 2000,2001, 2002,2003,
 *     2020,2021, 2022,2023,
 *
 *     2040,2041, 2042,2043,
 *     2060,2061, 2062,2063,
 *
 * のエリアに影響する。(d1,d0) (d3,d2)
 *                     (d5,d4) (d7,d6)
 *
 * 下位 0,1,...,7  -> 0,4,...,1c
 * 下位 0,8,...,38 -> 0,80,...,380
 */
	andi	t1, t3, 0x07
	sll	t1, t1, 2
	
	andi	t0, t3, 0x38
	sll	t0, t0, 4
	or	t0, t0, t1

	andi	t2, t2, 0x400
	or	t0, t0, t2
	addu	t0, t0, WORK

/* (d7,d6) -> draw vram + (0x40 と 0x60) + (2 と 3) */
	andi	t1, a1, 0xc0
	sb	t1, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x42(t0)
	sb	t1, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x43(t0)
	sb	t1, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x62(t0)
	sb	t1, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x63(t0)
/* (d5,d4) -> draw vram + (0x40 と 0x60) + (0 と 1) */
	andi	t2, a1, 0x30
	sll	t2, t2, 2
	sb	t2, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x40(t0)
	sb	t2, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x41(t0)
	sb	t2, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x60(t0)
	sb	t2, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x61(t0)
/* (d3,d2) -> draw vram + (0x00 と 0x20) + (2 と 3) */
	andi	t3, a1, 0x0c
	sll	t3, t3, 4
	sb	t3, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x02(t0)
	sb	t3, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x03(t0)
	sb	t3, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x22(t0)
	sb	t3, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x23(t0)
/* (d1,d0) -> draw vram + (0x00 と 0x20) + (0 と 1) */
	sll	t4, a1, 6
	sb	t4, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x00(t0)
	sb	t4, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x01(t0)
	sb	t4, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x20(t0)
	sb	t4, OFFSET_GFXBUF + OFFSET_GFXBUF_DRAW_VRAM + KS_RFC_VRAM_SIZE + 0x21(t0)
	
/* thru!! */
	GLOBAL(store_2007_done)
store_2007_done:
/* 縦書きか、横書きかで ppu_addr の増え方が変わる。
 * load_ppu からも来る。
 */
	lbu	t0, OFFSET_PPU2000H(WORK)
	lhu	t1, OFFSET_PPU_ADDR(WORK)
	andi	t0, t0, 0x04
	addiu	t1, t1, 1
	beq	t0, zero, 1f      /* 横書き */
	addiu	t1, t1, 0x20 - 1  /* 縦書き */
1:
	andi	t1, t1, 0x3fff
	sh	t1, OFFSET_PPU_ADDR(WORK)
#if 0
/* debug */
	move	a1,t1
	PRINTF("next ppu_addr=%x\n");
#endif
	jr	STORE_DONE

	GLOBAL(store_4014)
store_4014:
/* oam DMA
 * a1: src emu-cpu addr high
 * a1 < 0x20 なら internal wram
 * a1 >= 0x60 なら page table 参照。
 * 他は 無視。
 * t0: dst
 * t1: src
 * t2: src ptr end
 */
#if 0
/* debug */
	lhu	a2, OFFSET_LINECNT(WORK)
	PRINTF("DMA=%x at line %x\n");
#endif
	
	sltiu	t0, a1, 0x20
	andi	t1, a1, 0x07
	sll	t1, t1, 8
	addu	t1, t1, WORK
#if OFFSET_WRAM
	addiu	t1, t1, OFFSET_WRAM
#endif
	bne	t0, zero, 1f  /* internal wram */
	
	sltiu	t0, a1, 0x60
	sll	t1, a1, 8
	bne	t0, zero, store_done
	srl	t2, t1, 13
	sll	t2, t2, 2
	addu	t2, t2, WORK
	lw	t2, OFFSET_PAGE_TABLE(t2)
	addu	t1, t1, t2
1:
	move	t0, WORK
	addiu	t2, t0, 0x100
2:
	ld	t3, 0(t1)
	ld	t4, 8(t1)
	sd	t3, OFFSET_OAM + 0(t0)
	sd	t4, OFFSET_OAM + 8(t0)
	addiu	t1, t1, 0x10
	addiu	t0, t0, 0x10
	bne	t0, t2, 2b
/* OAM DMA は 512 clock 消費する。
 * んとちゃうの ?
 */
	li	t0, 0x200 * 0x100
	addu	LINECYCLE, LINECYCLE, t0
	jr	STORE_DONE
	
	GLOBAL(store_2004)
store_2004:
/* oam に書き込み - 使わないとは思うけど、一応実装しとかないとね。
 */
	lbu	t0, OFFSET_PPU2000H + 3(WORK)  /* oam addr */
	addu	t1, t0, WORK
	sb	a1, OFFSET_OAM(t1)
	addiu	t0, t0, 1
	sb	t0, OFFSET_PPU2000H + 3(WORK)
	jr	STORE_DONE

	GLOBAL(load_invalid)	
load_invalid:
#if 0
/* debug */
	TRAP("load invalid");
#endif
#if 0
/* debug */
	move	a1, a0
	move	a2, PC
	lhu	a3, OFFSET_LINECNT(WORK)
	PRINTF("invalid load(%x)PC=%x,line=%x\n");
#endif
	srl	v0, a0, 8
	jr	ra

	GLOBAL(store_invalid)
store_invalid:
#if 0
/* debug */
	TRAP("store invalid");
#endif
/*	j	store_verbose */
	jr	STORE_DONE

	GLOBAL(load_sound)
load_sound:
	sltiu	t0, a0, 0x40a0
	beq	t0, zero, load_invalid

/* kmar.rda, kaemario.fam で 4015R してるよ。
 */
#if 0
/* debug */
	TRAP("load sound");
#endif
	sw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	sw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	sw	gp, OFFSET_THREAD_GP(WORK)

	sw	ra, OFFSET_STACK_RA_SOUND(sp)
	sw	a0, OFFSET_STACK_A0(sp)
	sw	v1, OFFSET_STACK_V1(sp)
	sw	JUMPTBLP, OFFSET_THREAD_JUMPTBLP(WORK)
	sw	PAGE_PTR, OFFSET_THREAD_PAGE_PTR(WORK)

	jal	Sound_Read
	
	lw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	lw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	lw	gp, OFFSET_THREAD_GP(WORK)
	
	lw	ra, OFFSET_STACK_RA_SOUND(sp)
	lw	a0, OFFSET_STACK_A0(sp)
	lw	v1, OFFSET_STACK_V1(sp)
	lw	JUMPTBLP, OFFSET_THREAD_JUMPTBLP(WORK)
	lw	PAGE_PTR, OFFSET_THREAD_PAGE_PTR(WORK)
	jr	ra


	GLOBAL(store_2000)
store_2000:
/* t0 に a0 & 7
 * t1 に t0 + WORK
 * draw sccv new に MSBV,H をセットする。
 * draw sccv の MSBH だけは即反応 ?
 * 前回の 2000H と d4 が違えば BG, d3 が違えば OBJ bank change.
 */
	lbu	t3, OFFSET_PPU2000H(WORK)
	sb	a1, OFFSET_PPU2000H(WORK)
	andi	t0, a1, 0x03  /* MSBV | MSBH */

/* debug
 * cluclu.nes と jaleco/ninjakun.nes の互換性は ?
 */
	sb	t0, OFFSET_DRAW_SCCV_NEW(WORK)


	lbu	t1, OFFSET_DRAW_SCCV(WORK)
	andi	t0, t0, 0x01  /* MSBH */
	andi	t1, t1, 0xfe
	or	t0, t0, t1
	sb	t0, OFFSET_DRAW_SCCV(WORK)

#if 0
/* debug */
	lhu	a2, OFFSET_LINECNT(WORK)
	move	a3, LINECYCLE
	PRINTF("2000=%x at line %x--%x\n");
#endif
#if 0
/* debug */
	lhu	t2, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	andi	t1, t2, 0x400
	teq	t1, zero
#endif
	xor	t3, t3, a1
	andi	t2, t3, 0x10
	andi	t1, t3, 0x08
	beq	t2, zero, 1f
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
1:
	beq	t1, zero, store_done
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	GLOBAL(store05_io)
store05_io:
/* MMC5
 * < 0x5000 なら store_io へ。
 * < 0x5016 なら store_sound へ。
 * >= 5c00 なら exram 書き込み
 * < 0x5100 なら ignore
 * < 0x5131 なら index jump
 * < 0x5200 なら ignore
 * >= 0x5207 なら ignore
 * 5200-5206 は CPU ver, 乗算 など
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("05_io(%x->%x)\n");
#endif
	.set	noreorder
	sltiu	t0, a0, 0x5000
	bne	t0, zero, store_io
	sltiu	t0, a0, 0x5016
	bne	t0, zero, store_sound
	sltiu	t0, a0, 0x5c00
	beq	t0, zero, store05_5c00
	sltiu	t0, a0, 0x5100
	bne	t0, zero, store_invalid
	sltiu	t0, a0, 0x5131
	bne	t0, zero, 1f
	sltiu	t0, a0, 0x5200
	bne	t0, zero, store_invalid
	sltiu	t0, a0, 0x5207
	beq	t0, zero, store_ignore  /* o/enix/justbred.nes が 5800 に書き込んでいる */
/* 5200-5202 は RAM として。
 */
	addiu	t1, a0, -0x5203
	bltz	t1, store05_5c00_common
	addiu	t0, a0, -0x5200 + 0x400 /* ここで 5c00-5fff の後になるよう offset 調整 */

	addiu	t2, a0, -0x5205
	bltz	t2, 3f
/*
 * 5205 は multiple trigger
 * 5206 は multiple source
 */
	addiu	t2, a0, -0x5205
	bne	t2, zero, 2f
	lbu	t0, OFFSET_MMC5_MULT_SRC(WORK)
	mul	t0, t0, a1
	.set	reorder
/* 5205 multiple trigger
 * t0: src -> result
 */
	srl	t1, t0, 8
	sb	t0, OFFSET_MMC5_EXRAM + 0x400 + 0x05(WORK) /* result low */
	sb	t1, OFFSET_MMC5_EXRAM + 0x400 + 0x06(WORK) /* result high */
	jr	STORE_DONE
2: /* 5206 multiple source */
	sb	a1, OFFSET_MMC5_MULT_SRC(WORK)
	jr	STORE_DONE
1:
/* 5100-5131, index jump
 * t0 に amode_pamode (fixed)
 * t1 に 何か入れたい。
 */
	andi	t0, a0, 0x3f
	sll	t0, t0, 2
	addu	t0, t0, WORK
	lw	t2, OFFSET_MMC5_STOREFUNC_TBL(t0)
	lhu	t0, OFFSET_MMC5_AMODE_PAMODE(WORK)
	jr	t2
3:
/* 5203,5204
 * 5203: V 位置割り込み
 * 5204: d7: 割り込み enable
 * t1: a0 - 0x5203
 */
	andi	t0, a1, 0x80
	beq	t1, zero, 33f
	lbu	t1, OFFSET_SUN5_IRQ_L(WORK)
	sb	t0, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	sh	t1, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE
33:
	sb	a1, OFFSET_SUN5_IRQ_L(WORK)
	sh	a1, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE

	GLOBAL(store05_5c00)
store05_5c00:
/* MMC5 exram
 */
	addiu	t0, a0, -0x5c00

	GLOBAL(store05_5c00_common)
store05_5c00_common:
	addu	t0, t0, WORK
	sb	a1, OFFSET_MMC5_EXRAM(t0)
	jr	STORE_DONE

	GLOBAL(store13_io)
store13_io:
/* Namcot 106
 * 5000-5fff は IRQ
 */
	sltiu	t0, a0, 0x5000
	andi	t1, a0, 0x0800
	bne	t0, zero, store_io
	srl	t1, t1, 11
	j	store13_5000
	
	GLOBAL(storee1_io)
storee1_io:
/* 110 in 1
 * 5800-5fff に 4 bytes だけ low 4bits の RAM がある。
 */
	addiu	t0, a0, -0x5800
	andi	t1, a0, 0x0003
	bltz	t0, store_io
	addu	t1, t1, WORK
	sb	a1, OFFSET_BBRAM(t1)
	jr	STORE_DONE
		
	GLOBAL(store_io)
store_io:
/* 4000-5fff
 * 4000-4013, 4015: sound
 * 4014: OAM DMA
 * 4016: joypad
 * 4017: V ext mode
 * 4097 までは disksys サウンド関係がある。
 * 4020-4026: QD
 * funcptr を 多めに用意して 4027 以降を サウンドへ。
 */
	andi	t0, a0, 0x1fff
	sltiu	t1, a0, 0x4027
	beq	t1, zero, store_sound
	sll	t0, t0, 2
	addu	t0, t0, WORK
	lw	t0, OFFSET_STORE_IO_FUNCTBL(t0)
	jr	t0

/* a0 > 0xffff のときの特殊関数
 * 典型的には、 PC > 0xffff になったときと思われるので
 * PC に mask をかけて あとは internal wram の処理へ。
 */
	GLOBAL(load_internal_wram_10000)
load_internal_wram_10000:
	andi	PC, PC, 0xffff
	j	load_internal_wram

	GLOBAL(store_sound)
store_sound:
/* JUMPTBLP, PAGE_PTR は保存しなくてよい。
 * store 16bit なんてないし。
 * LINECYCLE, STORE_DONE をセーブ。
 * a2 に linecycle.
 */

#if 0
/* debug */
	xori	t0, a0, 0x4088
	bne	t0, zero, 1f
	sll	a1, a1, 1
	andi	a1, a1, 0x07
	PRINTF("<%02x>");
1:
#endif

	
#if 0
/* debug
 * disksys sound への書き込みを監視
 */
	xori	t0, a0, 0x4084
	beq	t0, zero, 4f
	xori	t0, a0, 0x4085
	bne	t0, zero, 9f
/* 4085 */
	beq	a1, zero, 8f
	j	8f

4:
/* 4084 */
	andi	t0, a1, 0xc0
	andi	t1, a1, 0x20
	andi	t2, a1, 0x1f
	sll	t2, t2, 1
	or	t1, t1, t2
	or	a1, t0, t1
8:
	andi	a2, a0, 0x000f
	PRINTF("<%02x-%x>");
9:
#endif

#if 0
/* debug */
	move	a2, a0
	PRINTF("sound(%x->%x)\n");
#endif
#if 1
	xori	t0, a0, 0x4010
	bne	t0, zero, 1f

/* 4010H patch test
 * 前回と同じ値なら無視。
 */
	lhu	t1, OFFSET_WORK_4010H_OUTPUT(WORK)
	sh	a1, OFFSET_WORK_4010H_OUTPUT(WORK)
	beq	t1, a1, store_done
1:
#endif
store_sound_4023:
	sw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	sw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	sw	gp, OFFSET_THREAD_GP(WORK)

/* div マクロは パラメタが 2 個なら 最初の レジスタに 勝手に mflo を付け加えてしまう !!
 * a2 = a2 / t0 が やりたいのだ。
 * アセンブラによる 強制 0 除算チェックを省くためには
 * div[u] zero, a2, t0; mflo a2 で良い。
 */
	lw	a2, OFFSET_SOUNDCYCLE(WORK)
#if 0
/* debug なんで音が変になる ?
 */
	sra	LINECYCLE, LINECYCLE, 8
	addu	a2, a2, LINECYCLE
	addiu	a2, a2, (-(LINECYCLE_CONST0 + LINECYCLE_CONST1) / 0x100)  /* linecycle は 負から始まっている */
#endif
	lbu	t0, OFFSET_SOUND_CYCLE_DIVIDER(WORK)
	divu	zero, a2, t0
	mflo	a2
#if 0
/* debug */
	move	a3, LINECYCLE
	PRINTF("Sound_Write(reg,%x,%x)cycle=%x\n");
#endif

	jal	Sound_Write

	lw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	lw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	lw	gp, OFFSET_THREAD_GP(WORK)
	jr	STORE_DONE
	
	GLOBAL(store_4016)
store_4016:
/* d0=1 なら serial -> work へ copy
 */
#if 0
/* debug */
	move	a2, PC
	PRINTF("4016=%x at PC=%x\n");
#endif
	sb	a1, OFFSET_WORK_4016H(WORK)
	andi	t0, a1, 0x01
	beq	t0, zero, store_done

	ld	t0, OFFSET_SERIAL4016D1(WORK)
	ld	t1, OFFSET_SERIAL4017D5(WORK)
	ld	t2, OFFSET_SERIAL4017D3(WORK)
	ld	t3, OFFSET_SERIAL4017D1(WORK)

	sd	t0, OFFSET_WORK_4016D1(WORK)
	sd	t1, OFFSET_WORK_4017D5(WORK)
	sd	t2, OFFSET_WORK_4017D3(WORK)
	sd	t3, OFFSET_WORK_4017D1(WORK)
	jr	STORE_DONE

	GLOBAL(store_4017)
store_4017:
/* d7: V ext mode は謎。
 * d6: /(V int en)
 * 良く分からんので d7,d6=0,0 のときのみ V ext int 発生にしてみる。
 */
#if 0
/* debug */
	move	a2, PC
	PRINTF("4017=%x at PC=%x\n");
#endif
#if 0
/* debug */
	PRINTF("4017=%x\n");
#endif
	andi	t0, a1, 0xc0
	sb	t0, OFFSET_WORK_4017H(WORK)
	jr	STORE_DONE
	
	GLOBAL(load_4016)
load_4016:
#if 0
/* debug */
	move	a1, PC
	PRINTF("4016R at PC=%x\n");
#endif
	lbu	t0, OFFSET_WORK_4016H(WORK)
	lw	t1, OFFSET_WORK_4016D1(WORK)
	lw	t2, OFFSET_WORK_4016D0(WORK)
	srl	t3, t1, 30   /* d31 -> d1 */
	andi	t3, t3, 0x02
	srl	t4, t2, 31   /* d31 -> d0 */
	andi	t4, t4, 0x01
	or	t3, t3, t4
	ori	v0, t3, 0x40
/* need to shift ? */
	andi	t0, t0, 0x01
	bne	t0, zero, load_done
	sll	t1, t1, 1
	sll	t2, t2, 1
	sw	t1, OFFSET_WORK_4016D1(WORK)
	sw	t2, OFFSET_WORK_4016D0(WORK)
	jr	ra

	GLOBAL(load_4017)
load_4017:
#if 0
/* debug */
	move	a1, PC
	PRINTF("4017R at PC=%x\n");
#endif
	lw	t4, OFFSET_WORK_4017D4(WORK)
	lw	t3, OFFSET_WORK_4017D3(WORK)
	lw	t2, OFFSET_WORK_4017D2(WORK)
	lw	t1, OFFSET_WORK_4017D1(WORK)
	lw	t0, OFFSET_WORK_4017D0(WORK)

	srl	$1, t4, 27  /* d31 -> d4 */
	andi	v0, $1, 0x10
	srl	$1, t3, 28  /* d31 -> d3 */
	andi	$1, $1, 0x08
	or	v0, v0, $1
	srl	$1, t2, 29  /* d31 -> d2 */
	andi	$1, $1, 0x04
	or	v0, v0, $1
	srl	$1, t1, 30  /* d31 -> d1 */
	andi	$1, $1, 0x02
	or	v0, v0, $1
	srl	$1, t0, 31  /* d31 -> d0 */
	andi	$1, $1, 0x01
	or	v0, v0, $1
	ori	v0, v0, 0x40
	lbu	$1, OFFSET_WORK_4016H(WORK)
	andi	$1, $1, 0x01
	bne	$1, zero, load_done
	sll	t4, t4, 1
	sll	t3, t3, 1
	sll	t2, t2, 1
	sll	t1, t1, 1
	sll	t0, t0, 1
	sw	t4, OFFSET_WORK_4017D4(WORK)
	sw	t3, OFFSET_WORK_4017D3(WORK)
	sw	t2, OFFSET_WORK_4017D2(WORK)
	sw	t1, OFFSET_WORK_4017D1(WORK)
	sw	t0, OFFSET_WORK_4017D0(WORK)
	jr	ra

	GLOBAL(load_4015)
load_4015:

	lbu	v0, OFFSET_WORK_4015H_OUTPUT(WORK)
#if 0
	sw	ra, OFFSET_THREAD_RA(WORK)
	sw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	sw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	sw	gp, OFFSET_THREAD_GP(WORK)
	jal	Sound_Read
	
	lw	ra, OFFSET_THREAD_RA(WORK)
	lw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	lw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	lw	gp, OFFSET_THREAD_GP(WORK)
	lbu	t0, OFFSET_WORK_4015H_OUTPUT(WORK)
	andi	t0, t0, 0xc0
	or	v0, v0, t0
#endif
	
#if 0
/* debug */
	move	a1, v0
	move	a2, PC
	PRINTF("4015R=%x at PC=%x\n");
#endif

/* d7,d6 は read-reset */
	andi	t0, v0, 0x3f
	sb	t0, OFFSET_WORK_4015H_OUTPUT(WORK)
	jr	ra
		
/* mapper-dependent funcs...
 */
	GLOBAL(store01_8000)
store01_8000:
/* MMC1
 * 8000-ffff 共通部分が多そうなので。
 * 書き込みデータ d7=1 で r0 の初期化 -> d3,d2 のみ 1 に。
 */
#if 0
/* debug */
	move	a2, a0
	move	a3, PC
	PRINTF("MMC1(%02x->%04x)PC=%x\n");
	bne	a1, zero, 1f
	sw	PC, OFFSET_TRACE_FLAG(WORK)
1:
#endif
	lbu	t1, OFFSET_MMC1_SHIFTCOUNT(WORK)
	andi	t0, a1, 0x80
	bne	t0, zero, 80f
	lbu	t2, OFFSET_MMC1_SHIFTREG(WORK)
	sll	t0, a1, 7  /* 0x01 -> 0x80 */
	srl	t2, t2, 1
	or	t2, t2, t0
	addiu	t1, t1, -1
	beq	t1, zero, 1f
	sb	t1, OFFSET_MMC1_SHIFTCOUNT(WORK)
	sb	t2, OFFSET_MMC1_SHIFTREG(WORK)
	jr	STORE_DONE
1:
/* 5bits 書き込み完了。*/
	li	t1, 5
	sb	t1, OFFSET_MMC1_SHIFTCOUNT(WORK)
	srl	t0, t2, 3
	andi	t0, t0, 0x1f
#if 0
/* debug */
	move	a1, a0
	move	a2, t0
	PRINTF("MMC1 %04x=%02x\n");
#endif
	andi	t1, a0, 0x6000
	srl	t2, t1, 13
	addu	t2, t2, WORK
	lbu	t3, OFFSET_MMC1_R0(t2)
	sb	t0, OFFSET_MMC1_R0(t2)
	beq	t3, t0, store_done  /* 同じ値の書き込みは無視 */

	xori	t2, t1, 0x6000
	beq	t1, zero, store01_r0
	bne	t2, zero, 1f  /* chrbank 変更 */
/* rombank
 * d4=1 なら もとの (rombank) d3 は保存。
 *   ... じゃなくて、 bank change を無視してみる。
 *   ... でも、やっぱ飛ぶ (sutte_ha.nes)
 */
	lbu	t3, OFFSET_MMC1_ROMBANK(WORK)
	andi	t1, t0, 0x10
	bne	t1, zero, 2f
	move	t3, t0
2:
/* debug rambank は未対応 */
	andi	t3, t3, 0x08
	andi	t1, t0, 0x07
	or	t3, t3, t1
	lbu	t2, OFFSET_PBANK_MASK(WORK)
	srl	t2, t2, 1
	and	t3, t3, t2
	sb	t3, OFFSET_MMC1_ROMBANK(WORK)
	lw	t1, OFFSET_PRGROM(WORK)
	lw	$1, OFFSET_MMC1_BIGROM(WORK)
	addu	t1, t1, $1
/* r0 の d3,d2 によって page table の設定
 * t1: prgrom
 * t3: new rombank
 */
	lbu	t0, OFFSET_MMC1_R0(WORK)
	andi	t2, t0, 0x08
	beq	t2, zero, 8f
/* d3=1 なので 0x4000 bytes/bank, d2 によって c000- or 8000- のみ変更 */
	andi	t0, t0, 0x04
	xori	t0, t0, 0x04  /* 常駐が 0x8000- なら 可変は 0xc000- */
	addiu	t2, t0, 0x08  /* 後の +0x8000 に相当 */
	sll	t2, t2, 12
	sll	t0, t0, 1
	addu	t0, t0, WORK
	mul	t3, t3, 0x4000
	addu	t3, t3, t1
	subu	t3, t3, t2
	sw	t3, OFFSET_PAGE_TABLE + 0x10(t0)
	sw	t3, OFFSET_PAGE_TABLE + 0x14(t0)
	jr	STORE_DONE
8:
/* d3=0 なので 0x8000 bytes/bank. rombank によって 8000-ffff の変更 */
	andi	t3, t3, 0x0e
	mul	t3, t3, 0x4000
	addu	t3, t3, t1
	addiu	t3, t3, -0x8000
	sw	t3, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x1c(WORK)
	jr	STORE_DONE
	
1:
/* chrbank の変更
 * t0: new chrbank
 * t1: 0x2000 (r1) or 0x4000 (r2)
 * r0 によって bank size が 0x2000 or 0x1000 になる。
 */
	xori	t1, t1, 0x2000
	lbu	t3, OFFSET_MMC1_R0(WORK)
	andi	t3, t3, 0x10
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	bne	t1, zero, 2f
/* r1 の書き込みだった。
 * r0 の d4=0 なら 0x2000 bytes/bank.
 *          1      0x1000 bytes/bank
 * t3: r0 の d4
 */

/* chrram なら dq4j.nes type かもよ... */
	lbu	$1, OFFSET_MMC1_DQ4_MODE(WORK)
	beq	$1, zero, 10f
	lw	$1, OFFSET_MMC1_BIGROM(WORK)
	li	t1, 0x80000
	and	t1, t1, $1
	andi	t0, t0, 0x10
	sll	t0, t0, 2 + 12  /* 0x10 -> 0x40000 */
	or	t0, t0, t1
	j	11f
10:

	addiu	t2, WORK, 2 * 4
	bne	t3, zero, 3f
/* 0x2000 bytes/bank なので t0 の d0 は無視 */
	andi	t0, t0, 0x1e
#if 1
/* とりあえず、必要 !!
 * zelda1j.nes が BG chrbank reg. に大きすぎる値をいれている。
 * これを入れないと、code_default/force_bss.c で確保している分を越える bss へ書き込まれる
 * (結果、audio routine で暴走したりする)
 * マスクしないといけないが、goemon1j.nes のことがあるので、現在は zelda1j.nes を非対応で。
 * いや、goemon1j.nes は mask しても動いていそう。
 * chrram で、bank 切り替えするものがあるけど、その判別方法が入ってない ...
 */
        andi t0,t0,0x00
#endif
	addiu	t2, WORK, 2 * 8
3:
	move	t1, WORK
	sll	t0, t0, 2
1:
	sh	t0, OFFSET_CHRBANK_REG0000(t1)
#if 0
/* debug */
	move	a2, t0
	subu	a3, t1, WORK
	PRINTF("MMC1 R1 %x,%x,%x\n");
#endif
	addiu	t0, t0, 1
	addiu	t1, t1, 2
	bne	t1, t2, 1b
	jr	STORE_DONE
2:
/* r2 の書き込みだった。
 * r0 の d4=0 なら無視。
 *          1 なら chr 1000- の変更
 */

/* chrram なら dq4j.nes type かもよ... */
	lbu	$1, OFFSET_MMC1_DQ4_MODE(WORK)
	beq	$1, zero, 10f
	lw	$1, OFFSET_MMC1_BIGROM(WORK)
	li	t1, 0x40000
	and	t1, t1, $1
	andi	t0, t0, 0x10
	sll	t0, t0, 2 + 13  /* 0x10 -> 0x80000 */
	or	t0, t0, t1
	j	11f
10:

	beq	t3, zero, store_done
	sll	t0, t0, 2

	move	t1, WORK
	addiu	t2, t1, 2 * 4
1:
	sh	t0, OFFSET_CHRBANK_REG1000(t1)
#if 0
/* debug */
	move	a2, t0
	subu	a3, t1, WORK
	PRINTF("MMC1 R2 %x,%x,%x\n");
#endif
	addiu	t0, t0, 1
	addiu	t1, t1, 2
	bne	t1, t2, 1b
	jr	STORE_DONE
11:
/* dq4 type の時に bigrom offset (0/0x40000/0x80000/0xc0000)
 * が変われば page table も変える。
 * $1: orig bigrom offset
 * t0: new  bigrom offset
 * $1:0,t0:0x40000 なら $1=0x40000 になるのでそれぞれ加える。
 */
	beq	$1, t0, store_done
	sw	t0, OFFSET_MMC1_BIGROM(WORK)
	subu	$1, t0, $1
	lw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	lw	t1, OFFSET_PAGE_TABLE + 0x18(WORK)
	addu	t0, t0, $1
	addu	t1, t1, $1
	sw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x1c(WORK)
#if 0
/* debug */
	move	a1, t0
	move	a2, t1
	move	a3, $1
	PRINTF("BIGROM %x,%x,%x\n");
#endif
	jr	STORE_DONE

80:
	li	t1, 5
	sb	t1, OFFSET_MMC1_SHIFTCOUNT(WORK)
	lbu	t0, OFFSET_MMC1_R0(WORK)
	ori	t0, t0, 0x0c
	GLOBAL(store01_r0)
store01_r0:
	sb	t0, OFFSET_MMC1_R0(WORK)
	lw	t3, OFFSET_PRGROM(WORK)
	lw	$1, OFFSET_MMC1_BIGROM(WORK)
	addu	t3, t3, $1
	lbu	t4, OFFSET_MMC1_ROMBANK(WORK)
/* d3 は ? */
	andi	t1, t0, 0x08
	beq	t1, zero, 8f
/* d3=1. つまり 0x4000 bytes/bank.
 * d2 によって 常駐バンク 0x8000- or 0xc000-
 */
	andi	t1, t0, 0x04
	addiu	t2, t1, 0x08  /* 後の +0x8000 に相当 */
	sll	t2, t2, 12
	subu	t2, t3, t2
	bne	t1, zero, 4f
/* 常駐バンク 8000-, 可変バンク c000- */
	sw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x14(WORK)
	addiu	t2, WORK, OFFSET_PAGE_TABLE + 0x18
	addiu	t3, t3, -0x4000 /* 後の -0xc000 に相当 */
	j	5f
4:
/* 常駐バンク c000-, 可変バンク 8000- */
	lw	$1, OFFSET_PRGSIZE(WORK)
#if 0
/* debug */
	move	a1, $1
	move	a2, t2
	move	a3, t3
	PRINTF("BIGROM2 %x,%x,%x\n");
#endif
	addu	t2, t2, $1
	addiu	t2, t2, -0x4000
	sw	t2, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x1c(WORK)
	addiu	t2, WORK, OFFSET_PAGE_TABLE + 0x10
5:
/* 可変バンクの設定 */
	addiu	t3, t3, -0x8000
	mul	t4, t4, 0x4000
	addu	t3, t3, t4
	sw	t3, 0(t2)
	sw	t3, 4(t2)
	j	6f
8:
/* d3=0. つまり 0x8000 bytes/bank.
 * rombank & 0x0e によって 8000-ffff.
 */
	addiu	t3, t3, -0x8000
	andi	t4, t4, 0x0e
	mul	t4, t4, 0x4000
	addu	t3, t3, t4
	sw	t3, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x1c(WORK)
6:
/* mirror
 * d1 によって one screen or 4 screen
 */
	andi	t1, t0, 0x02
	andi	t2, t0, 0x01
	mul	t2, t2, 0x400
	bne	t1, zero, 2f
/* one screen. d0 は ? */
	sh	zero, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	sh	t2, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	j	10f
2:
/* 4 screen. d0 によって 横持ち or 縦持ち */
	addiu	t2, t2, 0x400  /* もとの t2 は 0 or 0x400 */
	sh	zero, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	sh	t2, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
10:
/* chr bank size
 * d4 によって 0x2000 bytes/bank or 0x1000 bytes/bank
 */
#if 0
	lbu	t2, OFFSET_MMC1_IGNORE_CHRBANK(WORK)
	bne	t2, zero, store_done
#endif
	lbu	t2, OFFSET_MMC1_DQ4_MODE(WORK)
	bne	t2, zero, store_done
	
	andi	t1, t0, 0x10
	lbu	t2, OFFSET_MMC1_R1(WORK)
	bne	t1, zero, 20f
/* d4=0. つまり 0x2000 bytes/bank
 * r1 & 0x1e による。
 * max check は無視
 */
	andi	t2, t2, 0x1e
	sll	t2, t2, 2
	addiu	t3, t2, 4
	j	1f

20:
/* d4=1. つまり 0x1000 bytes/bank
 * r1, r2 による。
 * max check は無視
 */
	lbu	t3, OFFSET_MMC1_R2(WORK)
	sll	t3, t3, 2
	sll	t2, t2, 2
1:
	move	t0, WORK
	addiu	t1, t0, 2 * 4
2:
	sh	t2, OFFSET_CHRBANK_REG0000(t0)
	sh	t3, OFFSET_CHRBANK_REG1000(t0)
	addiu	t2, t2, 1
	addiu	t3, t3, 1
	addiu	t0, t0, 2
	bne	t0, t1, 2b

	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	
	GLOBAL(store02_8000)
store02_8000:
/* PRG1M, fixC000
 * 8000-bfff の pbank
 */
#if 0
/* debug */
	move	a2, a0
	move	a3, PC
	PRINTF("02(%x->%x)PC=%x\n");
#endif
	lw	t2, OFFSET_PRGROM(WORK)
	lbu	t3, OFFSET_PBANK_MASK(WORK)
	srl	t3, t3, 1   /* 0x4000 bytes/bank */
	and	t3, t3, a1
	addiu	t3, t3, -2  /* 後の -0x8000 に相当 */
	mul	t3, t3, 0x4000
	addu	t2, t2, t3
	sw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x14(WORK)
	jr	STORE_DONE
	
	GLOBAL(store03_6000)
store03_6000:
/* CHR128/256
 * 6000-ffff の書き込みで d0,d1 が chr bank になる。
 * chr bank は 0..3 (* 0x2000) なので obj/bg chr bank index を固定割り当て。
 */
#if 0
/* debug */
	move	a2, PC
	PRINTF("03_6000=%x at PC=%x\n");
#endif
	andi	t0, a1, 0x03
	mul	t1, t0, 0x2000
	lw	t2, OFFSET_CHR_OFFSET_MASK(WORK)
	and	t2, t2, t1
	srl	t2, t2, 10  /* / 0x400 */
/* t2 は 0, 8, 0x10, 0x18 */
	lhu	t3, OFFSET_CHRBANK_REG0000(WORK)
	move	t0, WORK
	beq	t3, t2, store_done
/* bg chr bank index は 8 bytes  (2*4) 単位、
 * (でも PPU 2000H BG chr seg があるので 16 bytes 単位で。)
 * obj               は 16 bytes (2*8) 単位。
 */
#if 0
/* debug */
	move	a1, t2
	PRINTF("03_chrbank=%x\n");
#endif
	sll	t3, t2, 1
/*	sb	t3, OFFSET_BG_CHRBANK_CHANGE_COUNT(WORK) */
/*	sb	t3, OFFSET_OBJ_CHRBANK_CHANGE_COUNT(WORK) */
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
			
	addiu	t1, t0, 2 * 8
1:
	sh	t2, OFFSET_CHRBANK_REG0000(t0)
	addiu	t2, t2, 1
	addiu	t0, t0, 2
	bne	t0, t1, 1b
	
	jr	STORE_DONE

	GLOBAL(store04_8000)
store04_8000:
/* address & 1 によって pointer or レジスタ設定 */
#if 0
/* debug */
	move	a2, a0
	move	a3, PC
	PRINTF("MMC3 %x->%x,%x\n");
#endif
	lbu	$1, OFFSET_MMC3_POINTER(WORK)
	andi	t1, a0, 0x0001
	bne	t1, zero, 10f
/* MMC3 pointer - chr seg, map mode, chr bank reg 設定
 */
	sb	a1, OFFSET_MMC3_POINTER(WORK)
/* chrrom 読み出し用 chr seg 設定 */
	andi	t2, a1, 0x80
	sll	t2, t2, 5  /* 0x80 -> 0x1000 */
	sh	t2, OFFSET_CHRSEG_FOR_READ(WORK)
	xor	t0, $1, a1

	andi	t1, t0, 0x80
	beq	t1, zero, 1f
/* chr seg が変わったので BG, OBJ bank が変わったと考える。*/
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
1:
	andi	t1, t0, 0x40
	beq	t1, zero, store_done
/* map mode が変わった。
 */
	lbu	t1, OFFSET_MMC3_R6(WORK)
	lw	t3, OFFSET_PRGROM(WORK)
	lw	t4, OFFSET_PRGSIZE(WORK)
	andi	t2, a1, 0x40
	bne	t2, zero, 41f
/* map mode 0 は window 0 (R6) が 8000-9fff. c000-dfff は prgrom + prgsize - 0x4000 固定。*/
	addiu	t1, t1, -4  /* 後で -0x8000 相当になる */
	mul	t1, t1, 0x2000
	addu	t1, t1, t3
	sw	t1, OFFSET_PAGE_TABLE + 0x10(WORK)
	addu	t3, t3, t4
	li	t4, -0x4000 - 0xc000
	addu	t3, t3, t4
	sw	t3, OFFSET_PAGE_TABLE + 0x18(WORK)
	jr	STORE_DONE
41:
/* map mode 1 は window 0 (R6) が c000-dfff. 8000-9fff は prgrom + prgsize - 0x4000 固定。*/
	addiu	t1, t1, -6  /* 後で -0xc000 相当になる */
	mul	t1, t1, 0x2000
	addu	t1, t1, t3
	sw	t1, OFFSET_PAGE_TABLE + 0x18(WORK)
	addu	t3, t3, t4
	li	t4, -0x4000 - 0x8000
	addu	t3, t3, t4
	sw	t3, OFFSET_PAGE_TABLE + 0x10(WORK)
	jr	STORE_DONE
	
10:
	
/* レジスタ設定
 * $1: pointer
 * t0: pointer & 7
 * t1: t0 + WORK
 * t2: 前回書いた値
 */
	andi	t0, $1, 0x07
	
	addu	t1, t0, WORK
	lbu	t2, OFFSET_MMC3_R0(t1)
	beq	t2, a1, store_done     /* 前回と同じ値 */
/* R0,R1 なら d0 を無視して chr bank for 0000-, 0800- を。
 * R2-R5 なら chr bank for 1000-,1400-,1800-,1c00- を。
 * R6,R7 なら prg bank.
 */
	lbu	t2, OFFSET_PBANK_MASK(WORK)
	addiu	t3, t0, -6
	bgez	t3, 6f
	lw	t2, OFFSET_CHR_OFFSET_MASK(WORK)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	mul	t4, a1, 0x400
#if 0
/* debug */
	ori	a2, t4, 0x3ff
	and	a3, a2, t2
	tne	a2, a3
#endif
	and	t4, t4, t2
	srl	t4, t4, 10  /* / 0x400 */
	addiu	t3, t0, -2
	bgez	t3, 2f
/* R0,R1 - d0 は無効 */
	andi	t4, t4, 0xfffe
	sb	t4, OFFSET_MMC3_R0(t1)
	sll	t0, t0, 2
	addu	t0, t0, WORK
	sh	t4, OFFSET_CHRBANK_REG0000(t0) /* 0000 or 0800 */
	addiu	t4, t4, 1
	sh	t4, OFFSET_CHRBANK_REG0400(t0) /* 0400 or 0c00 */
#if 0
/* debug */
	subu	a2, t0, WORK
	move	a3, t4
	PRINTF("R0,1 %x,%x,%x\n");

/* chr bank table が破壊されている。
 * t0 が 0 のとき trace flag ON!
 * ... デバッグ用 PRINTF で a0 とか引数にしてるけど、64bit operation の
 * 途中だったのがまずかった。32bit operation にした。
 */
	bne	a2, zero, 11f
	sw	WORK, OFFSET_TRACE_FLAG(WORK)
11:
#endif
	
	jr	STORE_DONE
2:
/* R2-R5
 * chr bank for 1000-, 1400-, 1800-, 1c00- を。
 * t3: t0 - 2
 */
	sb	t4, OFFSET_MMC3_R0(t1)
	sll	t3, t3, 1
	addu	t3, t3, WORK
	sh	t4, OFFSET_CHRBANK_REG1000(t3)
	jr	STORE_DONE
6:
/* R6-R7 pbank.
 * R6 なら mapmode によって window が違う。
 * $1: pointer
 * t0: pointer & 7
 * t1: t0 + WORK
 * t2: pbank mask
 * t3: t0 - 6
 */
	lw	t0, OFFSET_PRGROM(WORK)
	and	t2, t2, a1
	sb	t2, OFFSET_MMC3_R0(t1)
	bne	t3, zero, 7f
/* R6 */
	andi	$1, $1, 0x40
	beq	$1, zero, 40f
/* map mode 1 - R6 は c000-dfff */
	addiu	t2, t2, -6  /* 後で -0xc000 相当になる */
	mul	t2, t2, 0x2000
	addu	t2, t2, t0
	sw	t2, OFFSET_PAGE_TABLE + 0x18(WORK)
	jr	STORE_DONE
40:
/* map mode 0 - R6 は 8000-9fff */
	addiu	t2, t2, -4  /* 後で -0x8000 相当になる */
	mul	t2, t2, 0x2000
	addu	t2, t2, t0
	sw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	jr	STORE_DONE
7:
/* R7 - mapmode にかかわらず a000-bfff */
	addiu	t2, t2, -5  /* 後で -0xa000 相当になる */
	mul	t2, t2, 0x2000
	addu	t2, t2, t0
	sw	t2, OFFSET_PAGE_TABLE + 0x14(WORK)
	jr	STORE_DONE

	GLOBAL(store04_a000)
store04_a000:
/* address & 1 によって mirror or bbram protect
 * debug bbram protect は無視
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("MMC3 %x->%x\n");
#endif
	andi	t0, a0, 0x0001
	bne	t0, zero, store_done
/* mirror は d0=0 なら 横持ち、d0=1 なら 縦持ち */
	li	t2, 0x400
	andi	t0, a1, 0x01
	sllv	t2, t2, t0
	sh	t2, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	jr	STORE_DONE
	
	
	GLOBAL(store04_c000)
store04_c000:
/* address & 1 によって JAM or P&SIT */
#if 0
/* debug */
	move	a2, a0
	PRINTF("MMC3 %x->%x\n");
#endif
	lh	t1, OFFSET_LINECNT(WORK)
	addiu	t2, t1, -239
	andi	t0, a0, 0x0001
	bne	t0, zero, 10f
/* MMC3 JAM
 * linecnt_irq preset が相当する。
 * line 0..239 なら 設定と同時に linecnt_irq 再計算
 *   linecnt_irq = linecnt + a1 + 1
 */
	sh	a1, OFFSET_LINECNT_IRQ_PRESET(WORK)
	bltz	t1, store_done
	addiu	t3, a1, 1
	bgtz	t2, store_done
2:
/* 下から来る。*/
	addu	t3, t3, t1
3:
/* 下から来る。*/
	sh	t3, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE

10:
/* P&SIT (Preset & Start Interval Timer)
 * linecnt_irq が相当する。
 * line < 0 or line > 239 なら linecnt_irq = linecnt_irq preset を。
 * それ以外, つまり line 0..239 は やってほしくないけど
 *   linecnt_irq = linecnt + linecnt_irq preset + 1 かな?
 */
	lhu	t3, OFFSET_LINECNT_IRQ_PRESET(WORK)
	bltz	t1, 3b
	bgtz	t2, 3b
	
	addu	t1, t1, 1
	j	2b

	GLOBAL(store04_e000)
store04_e000:
/* address & 1 によって DITIRQ or EITIRQ */
#if 0
/* debug */
	move	a2, a0
	PRINTF("MMC3 %x->%x\n");
#endif
	andi	t0, a0, 0x0001
	sb	t0, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	jr	STORE_DONE

	GLOBAL(linecnt_over04)
linecnt_over04:
/* MMC3 ラインカウンタオーバー
 * line 0..239 なら 自動プリセットなので linecnt_irq 再計算
 *   linecnt_irq = linecnt + linecnt_irq preset + 1
 * それ以外なら 起こってはいけないが、無視。
 *   以降 カウンタオーバーが発生しないようにする。
 * BG, OBJ 両方がブランキングなら 無視。
 *   以降 カウンタオーバーが発生しないようにする。(smario3.nes)
 * DITIRQ なら カウンタオーバーは無視。
 *
 * t4: new linecnt
 * t3: linecnt_irq preset 値
 * t2: linecnt_irq enabled
 */
	addiu	t1, t4, -239
	bltz	t4, 2f
	addiu	t3, t3, 1
	bgtz	t1, 2f
	lbu	t0, OFFSET_PPU2000H + 1(WORK)
	addu	t3, t3, t4
	sh	t3, OFFSET_LINECNT_IRQ(WORK)
	beq	t2, zero, mainloop_srlt0

	andi	t0, t0, 0x18  /* BGBLK | OBJBLK */
	beq	t0, zero, 2f
	j	check_irq
2:
/* カウンタオーバー無視 */
	li	t0, 0x8000
	sh	t0, OFFSET_LINECNT_IRQ(WORK)
	j	mainloop_srlt0

	GLOBAL(check_irq)
check_irq:
/* cli なら 割り込み処理。
 * sei なら irq cause bit d5 を立てておく
 * mainloop_srlt0 へ戻る。
 */
	la	STORE_DONE, mainloop_srlt0
	lbu	t2, OFFSET_IRQ_CAUSE(WORK)
	andi	t2, t2, 0xff ^ 0x20
	sb	t2, OFFSET_IRQ_CAUSE(WORK)
	lbu	t0, OFFSET_WORK_RBDI(WORK)
	andi	t1, t0, 0x04  /* I */
	beq	t1, zero, activate_intr_fffe

	ori	t2, t2, 0x20	
	sb	t2, OFFSET_IRQ_CAUSE(WORK)
	j	mainloop_srlt0


	GLOBAL(store05_5100)
store05_5100:
/* 5100: amode
 * t0 に amode_pamode が入っている。
 * 今と同じなら 無視
 * br3: 6000-7fff (debug not yet)
 */
	sll	t3, a1, 8
	xor	t2, t3, t0
	andi	t2, t2, 0x0300
	andi	a2, t3, 0x0300
	beq	t2, zero, store_done
	lw	t1, OFFSET_PRGROM(WORK)
	addiu	t1, t1, -0x8000
	lbu	t2, OFFSET_PBANK_MASK(WORK)
	lbu	t3, OFFSET_MMC5_BR5(WORK)
	lbu	t4, OFFSET_MMC5_BR7(WORK)
	sb	a1, OFFSET_MMC5_AMODE_PAMODE(WORK)
	xori	$1, a2, 0x0100
	beq	a2, zero, store_invalid  /* amode 0 not yet */
	xori	a3, a2, 0x0200
	beq	$1, zero, 10f  /* amode 1 */
	beq	a3, zero, 20f  /* amode 2 */

	j	store_invalid
10:
/* amode 1
 * br5: 8000-bfff
 * br7: c000-ffff
 * bank no. の d0 は無視
 * br7 の d7=1 (にされている)
 * t0: temp
 * t1: prgrom - 0x8000
 * t2: pbank mask
 * t3: br5
 * t4: br7
 */
	and	t3, t3, t2
	andi	t3, t3, 0xfe
	mul	t3, t3, 0x2000
	addu	t3, t3, t1
	sw	t3, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x14(WORK)

	and	t4, t4, t2
	andi	t4, t4, 0xfe
	mul	t4, t4, 0x2000
	addu	t4, t4, t1
	addiu	t4, t4, -0x4000
	sw	t4, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t4, OFFSET_PAGE_TABLE + 0x1c(WORK)

	jr	STORE_DONE

20:
/* amode 2
 * br5: 8000-bfff
 * br6: c000-dfff
 * br7: e000-ffff
 * t0: br6
 * t1: prgrom - 0x8000
 * t2: pbank mask
 * t3: br5
 * t4: br7
 */
	lbu	t0, OFFSET_MMC5_BR6(WORK)
	and	t3, t3, t2
	andi	t3, t3, 0xfe
	mul	t3, t3, 0x2000
	addu	t3, t3, t1
	sw	t3, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x14(WORK)

	and	t0, t0, t2
	mul	t0, t0, 0x2000
	addu	t0, t0, t1
	addiu	t0, t0, -0x4000
	sw	t0, OFFSET_PAGE_TABLE + 0x18(WORK)

	and	t4, t4, t2
	mul	t4, t4, 0x2000
	addu	t4, t4, t1
	addiu	t4, t4, -0x6000
	sw	t4, OFFSET_PAGE_TABLE + 0x1c(WORK)
	jr	STORE_DONE

	GLOBAL(store05_5101)
store05_5101:
/* pamode
 * t0 に amode_pamode が入っている。
 */
#if 0
	PRINTF("05_pamode(%x)\n");
#endif
	xor	t2, a1, t0
	andi	t2, t2, 0x0003
	andi	a2, a1, 0x0003
	beq	t2, zero, store_done

	sb	a1, OFFSET_MMC5_AMODE_PAMODE + 1(WORK)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE
	

	GLOBAL(store05_5102)
store05_5102:
store05_5103:
/* 5102,5103: exram gate
 * 無視
 */
#if 0
	move	a2, a0
	PRINTF("05_exram_gate(%x->%x)ignored\n");
#endif
	jr	STORE_DONE

	GLOBAL(store05_5104)
store05_5104:
/* exram mode
 * 0: vram2
 * 1: at-vram
 * 2: cpu-wram
 * 3: write protect
 * 無視。
 */
#if 0
/* debug */
	PRINTF("05_exram_mode(%x)ignored\n");
#endif
	jr	STORE_DONE

	GLOBAL(store05_5105)
store05_5105:
/* vram bank
 * d7,d6=vbr 3
 * d5,d4=vbr 2
 * d3,d2=vbr 1
 * d1,d0=vbr 0
 * うーんこれって namcot 106 でもあったけど 単純に横持ち、縦持ちでは
 * 対応できんなあ。
 * しかも castlev3.nes が いきなり VRAM2 で 0x44 を書いている。
 * とりあえず namcot 106 のように
 * 00 01 00 01 -> 横持ち
 * 00 00 01 01 -> 縦持ち
 * 00 00 00 00 -> 1 画面 2000-
 * 01 01 01 01 ->        2400-
 */
#if 0
/* debug */
	PRINTF("05_vrambank(%x)ignored\n");
#endif
	jr	STORE_DONE

	GLOBAL(store05_5106)
store05_5106:
/* vram3 chr no
 * 無視。
 */
#if 0
/* debug */
	PRINTF("05_vram3chr(%x)ignored\n");
#endif
	jr	STORE_DONE

	GLOBAL(store05_5107)
store05_5107:
/* vram3 area code
 * 無視。
 */
#if 0
/* debug */
	PRINTF("05_vram3area(%x)ignored\n");
#endif
	jr	STORE_DONE

	GLOBAL(store05_5113)
store05_5113:
/* br3: 6000-7fff
 * 0x00-0x0f
 * 無視
 */
#if 0
/* debug */
	PRINTF("05_br3(%x)ignored\n");
#endif
	andi	t0, a1, 0x0f
	sb	t0, OFFSET_MMC5_BR3(WORK)
	jr	STORE_DONE

	GLOBAL(store05_5114)
store05_5114:
/* br4: 8000-9fff
 * t0 に amode_pamode
 * amode 3 のみ有効
 */
	andi	t0, t0, 0x0300
	xori	t0, t0, 0x0300
	lbu	t3, OFFSET_PBANK_MASK(WORK)
	bne	t0, zero, store_done
	lw	t4, OFFSET_PRGROM(WORK)
	and	t3, t3, a1
	mul	t3, t3, 0x2000
	addu	t3, t3, t4
	addiu	t3, t3, -0x8000
	sw	t3, OFFSET_PAGE_TABLE + 0x10(WORK)
	jr	STORE_DONE
	
	GLOBAL(store05_5115)
store05_5115:
/* br5
 * t0 に amode_pamode
 * amode 0 なら無視
 * amode 3 なら a000-bfff
 * 他は 8000-bfff
 */
	andi	t2, t0, 0x0300
	xori	t3, t2, 0x0300
	beq	t2, zero, store_done
	lw	t2, OFFSET_PRGROM(WORK)
	lbu	t4, OFFSET_PBANK_MASK(WORK)
	and	t4, t4, a1
	sb	a1, OFFSET_MMC5_BR5(WORK)
	beq	t3, zero, 3f
/* amode 1 or 2 */
	andi	t0, t4, 0xfe
	mul	t0, t0, 0x2000
	addiu	t0, t0, -0x8000
	addu	t0, t0, t2
	sw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x14(WORK)
	jr	STORE_DONE
3:
/* amode 3 */
	addiu	t0, t4, -5  /* 後の -0xa000 に相当 */
	mul	t0, t0, 0x2000
	addu	t0, t0, t2
	sw	t0, OFFSET_PAGE_TABLE + 0x14(WORK)
	jr	STORE_DONE

	GLOBAL(store05_5116)
store05_5116:
/* br6
 * t0 に amode_pamode
 * amode 0,1 なら無視
 * amode 2,3 なら c000-dfff
 */
	andi	t2, t0, 0x0200
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	beq	t2, zero, store_done
	lw	t2, OFFSET_PRGROM(WORK)
	sb	a1, OFFSET_MMC5_BR6(WORK)
	and	t1, t1, a1
	addiu	t1, t1, -6  /* 後の -0xc000 に相当 */
	mul	t1, t1, 0x2000
	addu	t1, t1, t2
	sw	t1, OFFSET_PAGE_TABLE + 0x18(WORK)
	jr	STORE_DONE

	GLOBAL(store05_5117)
store05_5117:
/* br7
 * t0 に amode_pamode
 * amode 0 なら 8000-ffff
 * amode 1 なら c000-ffff
 * amode 2,3 なら e000-ffff
 */
	andi	t2, t0, 0x0300
	andi	t0, t0, 0x0200
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	lw	t3, OFFSET_PRGROM(WORK)
	and	t1, t1, a1
	beq	t2, zero, 1f  /* amode 0 */
	beq	t0, zero, 2f  /* amode 1 */
/* amode 2,3 */
	addiu	t1, t1, -7  /* 後の -0xe000 に相当 */
	mul	t1, t1, 0x2000
	addu	t1, t1, t3
	sw	t1, OFFSET_PAGE_TABLE + 0x1c(WORK)
	jr	STORE_DONE
1:
/* amode 0 */
	andi	t1, t1, 0xfc
	mul	t1, t1, 0x2000
	addu	t1, t1, t3
	addiu	t1, t1, -0x8000
	sw	t1, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x1c(WORK)
	jr	STORE_DONE
2:
/* amode 1 */
	andi	t1, t1, 0xfe
	addiu	t1, t1, -6  /* 後の -0xc000 に相当 */
	mul	t1, t1, 0x2000
	addu	t1, t1, t3
	sw	t1, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x1c(WORK)
	jr	STORE_DONE
	
	GLOBAL(store05_5120)
store05_5120:
/* pbr0-7, pbr4'-7'
 * pbr4'-7' は 無視
 * amode による更新は 無視
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("05_pbr(%x->%x)\n");
#endif
	lhu	t1, OFFSET_MMC5_PBREX(WORK)
	andi	t0, a0, 0x0f
	sll	t0, t0, 1
	addu	t0, t0, WORK
	lhu	t2, OFFSET_MMC5_PBR0(t0)
	or	t1, t1, a1
	sh	t1, OFFSET_MMC5_PBR0(t0)
	beq	t1, t2, store_done
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	GLOBAL(store05_5130)
store05_5130:
/* pbrex */
	andi	t0, a1, 0x03
	sll	t0, t0, 8
	sh	t0, OFFSET_MMC5_PBREX(WORK)
	jr	STORE_DONE

	
	
#if 0
/* mappers.nfo まったくうそやん。*/
	GLOBAL(store06_4000)
store06_4000:
/* FFE F4xxx
 */
#if 0
/* debug */
	move	a2, a0
	move	a3, PC
	PRINTF("06(%x->%x),PC=%x\n");
#endif
	xori	t0, a0, 0x43fe
	bne	t0, zero, store_io
#if 0
/* debug */
	move	a2, a0
	move	a3, PC
	PRINTF("06(%x->%x),PC=%x\n");
#endif
	jr	STORE_DONE
#endif
	
	GLOBAL(store06_8000)
store06_8000:
/* FFE F4xxx
 * goemon1j.nes 用。
 * pbank for 8000-bfff かなあ? 値が 0x14 だから 違うっぽい。
 * d7-d2 が pbank ?
 * d0 が chrbank ? chrram なのに ?
 * そう。d1-d0 が chrbank
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("06(%x->%x),");
	move	a2,PC
	PRINTF("%x,PC=%x,");
	lhu	a2, OFFSET_LINECNT(WORK)
	PRINTF("%x,linecnt=%x\n");
#endif

	lw	t2, OFFSET_PRGROM(WORK)
	andi	t1, a1, 0x3c
	sll	t1, t1, 14 - 2  /* 1 << 14 = 0x4000 */
	addiu	t1, t1, -0x8000
	addu	t1, t1, t2
	sw	t1, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x14(WORK)

/* d1,d0 から 0, 8, 0x10, 0x18 (* 0x400)
 */
	andi	t2, a1, 0x03
	sll	t2, t2, 3
#if 0
/* debug */
	move	a1, t2
	PRINTF("06_chrbank(%x)\n");
#endif
	lhu	t1, OFFSET_CHRBANK_REG0000(WORK)
	move	t0, WORK
	beq	t2, t1, store_done

	addiu	t1, t0, 2 * 8
1:
	sh	t2, OFFSET_CHRBANK_REG0000(t0)
	addiu	t2, t2, 1
	addiu	t0, t0, 2
	bne	t0, t1, 1b
	
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	GLOBAL(store07_8000)
store07_8000:
/* Rare
 * d3-d0: pbank 0x8000 単位
 * d4: One Screen Mirroring
 *   0=mirror pages from PPU $2000
 *   1=mirror pages from PPU $2400
 * これって 1 画面モードしかないってこと ?
 */
	andi	t0, a1, 0x10
	sll	t0, t0, 2 + 4  /* 0x10 -> 0x400 */
	sh	t0, OFFSET_VRAM_MIRROR_MASK_OR(WORK)

	lw	t1, OFFSET_PRGROM(WORK)
	andi	t0, a1, 0x0f
	mul	t0, t0, 0x8000
	addu	t0, t0, t1
	addiu	t0, t0, -0x8000
	sw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x1c(WORK)
	jr	STORE_DONE


	GLOBAL(store09_a000)
store09_a000:
/* MMC2
 * a000-afff: pbank at 8000-9fff - max check は無視
 * b000-cfff: chr bank 0000-0fff
 */
	andi	t0, a0, 0x1000
	bne	t0, zero, store09_b000
	lw	t2, OFFSET_PRGROM(WORK)
	mul	t3, a1, 0x2000
	addiu	t3, t3, -0x8000
	addu	t2, t2, t3
	sw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	jr	STORE_DONE

	GLOBAL(store09_c000)
store09_c000:
/* MMC2
 * b000-cfff: chr bank 0000-0fff
 * d000-dfff: chr bank 1000-1fff when latch selector is $fd
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("09(%x->%x)\n");
#endif
	andi	t0, a0, 0x1000
	bne	t0, zero, store09_d000
store09_b000:
#if 0
/* debug */
	beq	t0, zero, 1f
	move	a2, a0
	PRINTF("09(%x->%x)\n");
1:
#endif
	sll	t1, a1, 4  /* 0x1000 bytes/bank */
	sh	t1, OFFSET_CHRBANK_REG0000(WORK)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG0400(WORK)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG0800(WORK)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG0C00(WORK)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE
	
store09_d000:
	sll	t1, a1, 4  /* 0x1000 bytes/bank */
	sh	t1, OFFSET_CHRBANK_REG1000(WORK)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG1400(WORK)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG1800(WORK)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG1C00(WORK)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)

	jr	STORE_DONE

	GLOBAL(store09_e000)
store09_e000:
/* MMC2
 * e000-efff: chr bank 1000-1fff when latch selector is $fe
 * f000-ffff: d0=mirror 0=横持ち 1=縦持ち
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("09(%x->%x)\n");
#endif
	andi	t0, a0, 0x1000
	bne	t0, zero, store09_f000

#if 0
/* debug */
	move	a2, a0
	PRINTF("09(%x->%x)\n");
#endif
	
	sll	t1, a1, 4  /* 0x1000 bytes/bank */
	sh	t1, OFFSET_CHRBANK_REG1000(WORK)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG1400(WORK)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG1800(WORK)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG1C00(WORK)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)

	jr	STORE_DONE
store09_f000:
	li	t1, 0x400
	andi	t0, a1, 0x01
	beq	t0, zero, 1f
	li	t1, 0x800
1:
	sh	t1, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	jr	STORE_DONE

	GLOBAL(store0f_8000)
store0f_8000:
/* 100 in 1
 * 8000: SMPPPPPP
 * S: 0: swap 8000-/a000-
 *    1: swap c000-/e000
 * M: mirror 横持ち、縦持ち
 * P: pbank @ 8000-bfff, c000-ffff
 * bank 番号は 0x4000 bytes 単位 ?
 *
 * 8001: SxPPPPPP
 * PPPPPP: pbank @ c000-ffff
 * S: Swap c000-/e000-
 *
 * 8002: SxPPPPPP
 * PPPPPP: select 8K of a 16K segment at
 *         8000-/a000-/c000-/e000-
 * S: Segment selector 0 - select lower 8K of segment
 *                     1 -        upper
 *
 * 8003: SMPPPPPP
 * PPPPPP: select 16k rom bank @ c000-ffff
 * M: mirror
 * S: swap 8K at c000-/e000-
 *
 * なんじゃこりゃー !?
 * 手抜きだけど、動いていそう。
 */
#if 0
/* debug */
	move	a2, a0
	move	a3, PC
	PRINTF("0f(%x->%x)PC=%x\n");
#endif

	.set	noreorder
	andi	t0, a0, 0x0003
	beq	t0, zero, 10f
	xori	t1, t0, 0x0002
	beq	t1, zero, 20f
	xori	t1, t0, 0x0003
	beq	t1, zero, 30f
	nop
	.set	reorder

/*
 * 8001 は実装してない
 */
	jr	STORE_DONE
10:
/* 8000
 * swap は実装してない
 */
	lw	t1, OFFSET_PRGROM(WORK)
	andi	t2, a1, 0x3f
	mul	t2, t2, 0x4000
	addu	t1, t1, t2
	addiu	t1, t1, -0x8000
	sw	t1, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t1, OFFSET_PAGE_TABLE + 0x1c(WORK)
	
15:
/* 下からも来る */
	andi	t0, a1, 0x40
	sll	t0, t0, 4
	addiu	t0, t0, 0x400
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)

	lw	a0, OFFSET_PAGE_TABLE + 0x18(WORK)
	li	t1, 0xc000
	addu	a0, a0, t1
	sw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	sw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	sw	gp, OFFSET_THREAD_GP(WORK)
	jal	Sound_SetC000
	lw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	lw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	lw	gp, OFFSET_THREAD_GP(WORK)
	jr	STORE_DONE

20:
/* 8002
 * ここに書き込むと、8000-,a000-,c000-,e000- どれも同じバンクの イメージ ?
 */
	lw	t1, OFFSET_PRGROM(WORK)
	andi	t2, a1, 0x3f
	mul	t2, t2, 0x4000
	andi	t3, a1, 0x80
	sll	t3, t3, 8 - 2
	addu	t2, t2, t3
	addu	t2, t2, t1

	addiu	t2, t2, -0x8000
	sw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	addiu	t2, t2, -0x2000
	sw	t2, OFFSET_PAGE_TABLE + 0x14(WORK)
	addiu	t2, t2, -0x2000
	sw	t2, OFFSET_PAGE_TABLE + 0x18(WORK)
	addiu	t2, t2, -0x2000
	sw	t2, OFFSET_PAGE_TABLE + 0x1c(WORK)
	jr	STORE_DONE

30:
/* 8003
 * swap は 無視
 * ボンバーマンを選んだとき set c000 しないと変
 * swap は実装してない
 */
	lw	t1, OFFSET_PRGROM(WORK)
	andi	t2, a1, 0x3f
	addiu	t2, t2, -3      /* 後の -0xc000 に相当 */
	mul	t2, t2, 0x4000
	addu	t2, t2, t1
	sw	t2, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x1c(WORK)
	j	15b

	
	
	GLOBAL(store12_8000)
store12_8000:
/* Jaleco SS8806
 * 8000,8001: pbank @ 8000-9fff low, high
 * 8002,8003:         a000-bfff
 * 9000,9001:         c000-dfff
 * address & 0x1000 -> 0,+4
 * address & 0x0003 -> + それ
 * +6 以上は無視
 * t0, t1: temp
 * t2: 0,1, 2,3, 4,5 -> 0, 1, 2 -> 0, 4, 8
 * t3: dst ptr
 * t4: other data ptr -> other data
 * t5: pbank mask
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("12(%x->%x),");
#endif
	andi	t0, a0, 0x1000
	srl	t0, t0, 12 - 2  /* 0x1000 -> 0x04 */
	andi	t1, a0, 0x0003
	addu	t2, t0, t1
	sltiu	t1, t2, 6
	addu	t3, t2, WORK
	beq	t1, zero, store_ignore /* shinmoe.nes が 9002,9003 に書き込んでいる */

	andi	t0, a0, 0x0001
	xor	t4, t3, 1
	lbu	t4, OFFSET_SS_PBANK0(t4) /* 今回設定と逆の方を読んでくる */
	andi	t1, a1, 0x0f
	beq	t0, zero, 1f
/* higher byte */
	sll	t1, t1, 4
1:
	lbu	t5, OFFSET_PBANK_MASK(WORK)
	sb	t1, OFFSET_SS_PBANK0(t3)
	or	t1, t1, t4
	and	t1, t1, t5
	
/* t1: new pbank */
	srl	t2, t2, 1
	subu	t4, t1, t2  /* -0x8000,-0xa000,-0xc000 にそなえて -0,-1,-2 する */
	addiu	t4, t4, -4  /* 後の -0x8000 に相当 */
	mul	t4, t4, 0x2000
	sll	t2, t2, 2   /* 0/1/2 -> 0/4/8 */
#if 0
/* debug */
	move	a1, t2
	move	a2, t1
	PRINTF("12_pbank(%x=%x)\n");
#endif
	lw	t5, OFFSET_PRGROM(WORK)
	addu	t5, t5, t4
	addu	t2, t2, WORK
	sw	t5, OFFSET_PAGE_TABLE + 0x10(t2)
	jr	STORE_DONE

	GLOBAL(store12_a000)
store12_a000:
/* Jaleco SS8806
 * a000,a001: chrbank @ 0000- low, high
 * a002,a003:           0400-
 * b000,b001:           0800-
 * b002,b003:           0c00-
 * c000,c001:           1000-
 * c002,c003:           1400-
 * d000,d001:           1800-
 * d002,d003:           1c00-
 * address & 0x3000 -> xor 0x2000 -> 0000/1000/2000/3000
 *   -> 0/4/8/c これが dst offset その 1.
 * address & 0x0003 -> + それ。dst offset その 2.
 * で、 (dst offset 1 + dst offset 2) & 0x0e が chrbank reg0000 からの offset.
 * t0, t1: temp
 * t2: dst offset 1 -> (dst offset 1 + dst offset 2) & 0x0e
 * t3: dst offset 2 -> dst ptr
 * t4: other data ptr -> other data
 */
	andi	t0, a0, 0x3000
	xori	t0, t0, 0x2000
	srl	t2, t0, 12 - 2  /* 0x3000 -> 0x0c */
	andi	t3, a0, 0x0003
	addu	t2, t2, t3
	addu	t3, t2, WORK
	xori	t4, t3, 0x01
	lbu	t4, OFFSET_SS_CHRBANK0(t4)
	andi	t0, a1, 0x0f
	andi	t1, a0, 0x0001
	beq	t1, zero, 1f
/* higher byte */
	sll	t0, t0, 4
1:
	sb	t0, OFFSET_SS_CHRBANK0(t3)
	or	t0, t0, t4
/* t0: new chrbank */
	andi	t2, t2, 0x0e
	addu	t2, t2, WORK
	sh	t0, OFFSET_CHRBANK_REG0000(t2)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE
	
	GLOBAL(store12_e000)
store12_e000:
/* Jaleco SS8806
 * e000,e001: irq L l,h
 * e002,e003: irq H l,h
 * f000,f001: d0=1: enable irq
 * f002: d1=1: one screen @ 2000-
 *       d1=0: d0 によって横持ち、縦持ち
 * f003: External I/O
 */
	andi	t0, a0, 0x1000
	andi	t1, a0, 0x0003
	bne	t0, zero, 10f
/* irq
 * address & 3 -> dst ptr
 */
#if 0
/* debug */
	move	a2,a0
	PRINTF("12_irq(%x->%x)\n");
#endif
	andi	t0, a0, 0x0003
	addu	t0, t0, WORK
	andi	t1, a1, 0x0f
	sb	t1, OFFSET_SS_IRQL(t0)
	jr	STORE_DONE
10:
	andi	t1, a0, 0x0002
	andi	t0, a1, 0x01
	bne	t1, zero, 2f
/* d0=1: enable irq */
	lbu	t1, OFFSET_SS_IRQH + 1(WORK)
	lbu	t2, OFFSET_SS_IRQH + 0(WORK)
	lbu	t3, OFFSET_SS_IRQL + 1(WORK)
	lbu	t4, OFFSET_SS_IRQL + 0(WORK)
	sll	t1, t1, 12
	sll	t2, t2, 8
	sll	t3, t3, 4
	or	t1, t1, t2
	or	t1, t1, t3
	or	t1, t1, t4
	sb	t0, OFFSET_LINECNT_IRQ_ENABLED(WORK)

	li	t0, (-(LINECYCLE_CONST0 + LINECYCLE_CONST1) / 0x100)
	divu	zero, t1, t0
	lh	t2, OFFSET_LINECNT(WORK)
	mflo	t0
	addu	t2, t2, t0
	sh	t2, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE

2:
/* f003 なら voice (shinmoe.nes)
 */
	andi	t1, a0, 0x0001
	andi	t2, a1, 0x02
	bne	t1, zero, 5f
/* mirror
 */
	sh	zero, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	bne	t2, zero, 2f
	mul	t0, t0, 0x400
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	jr	STORE_DONE
2:
/* one screen @ 2000- */
	sh	zero, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	jr	STORE_DONE

5:
/* f003: shinmoe.nes voice
 * 書き込む値は、おそらく喋っている間、同じ値を書き続ける。
 * 0x00: 初期化 ?
 * 0x01: 無音 ?
 *
 *
 * ボールを投げる音
 * 00,42,40....
 * ボール返球
 * 00,4a,48....
 * 歓声
 * 00,4e,4c...
 * フライキャッチ ?
 * 00,46,44...
 * ↑下と同時にいける ?
 * 「アウト」
 * 00,02,00...
 * 「セーフ」
 * 00,06,04...
 * 「ファール」
 * 00,0a,08...
 * 「フェア」
 * 00,0e,0c...
 * 「ストライク」
 * 00,12,10...,
 * 「ボール」
 * 00,16,14...., 
 * 「タイム」
 * 00,1a,18....
 * 「バッターアウト」
 * 00,1e,1c...
 * 「フォアボール」
 * 00,22,20...
 * 「ホームラン」
 * 00,26,24...
 * 「プレイボール」
 * 00,2a,28...
 * 「ピッチャー交代」
 * 00,2e,2c...
 * 「代打」
 * 00,32,30...
 * 「イテ」
 * 00,36,34...
 * 「カーン」
 * 00,3a,38...
 * 当ったときの、もう一つの音
 * 00,3e,3c...
 *
 * 0x40 台は 無視。
 * d1=1 のとき、d5,d4,d3,d2 によって。
 * 
 */
#if 0
/* debug */
	PRINTF("12(%x)\n");
#endif
	andi	t0, a1, 0x42
	xori	t2, a1, 0x4e
	beq	t2, zero, 10f
	xori	t0, t0, 0x02
	andi	t1, a1, 0x3c
	bne	t0, zero, store_ignore

	srl	t1, t1, 2
	
	.data
voice_id_table_12:
	.byte	0x4, 0x5, 0x6, 0x7, 0x1, 0x2, 0x3, 0x8
	.byte	0xa, 0xb, 0x9, 0xc, 0xe, 0xd, 0xf, 0xf
	.text
	
	la	t2, voice_id_table_12
	addu	t2, t2, t1
	lbu	a0, 0(t2)
9:
#if 0
/* debug */
	move	a2, a0
	PRINTF("12_voice(%x->%x)\n");
#endif

	sw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	sw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	sw	gp, OFFSET_THREAD_GP(WORK)
	jal	Sound_PlayMENUPCM
	lw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	lw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	lw	gp, OFFSET_THREAD_GP(WORK)
		
	jr	STORE_DONE
10:
/* 歓声 */
	li	a0, 0x10
	j	9b
	
	GLOBAL(store13_5000)
store13_5000:
/* Namcot 106
 * 5000-57ff: irq L
 * 5800-5fff: irq H
 * t1 が 0 or 1 で 5000-,5800- が分かる。
 * 5800=80,5000=0 を書いている。うごかん(kaijyu.nes)
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("13_irq(%x->%x)\n");
#endif
	addu	t0, t1, WORK
	sb	a1, OFFSET_SUN5_IRQ_L(t0)
	beq	t1, zero, store_done
/* High の d7: 1=enable
 * カウントアップして 0x7fff になったら IRQ
 */
	andi	t0, a1, 0x80
	sb	t0, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	beq	t0, zero, store_done

	lbu	t0, OFFSET_SUN5_IRQ_L(WORK)
	lbu	t1, OFFSET_SUN5_IRQ_H(WORK)
	lh	t2, OFFSET_LINECNT(WORK)
	sll	t1, t1, 8
	or	t1, t1, t0
	andi	t1, t1, 0x7fff
	li	t3, 0x7fff
	subu	t3, t3, t1
/* (-(LINECYCLE_CONST0 + LINECYCLE_CONST1) / 0x100) で割ると
 * 下が ちょん切れるんだけど。(rollingj.nes)
 */
	li	t1, (-(LINECYCLE_CONST0 + LINECYCLE_CONST1) / 0x100)
	divu	zero, t3, t1
	mflo	t0
	addu	t2, t2, t0
	sh	t2, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE
	
	GLOBAL(store13_8000)
store13_8000:
store13_a000:
/* Namcot 106
 * $8000-$87FF    Select a 1kB CHR-ROM page at $0000.
 * $8800-$8FFF    Select a 1kB CHR-ROM page at $0400.
 * $9000-$97FF    Select a 1kB CHR-ROM page at $0800.
 * $9800-$9FFF    Select a 1kB CHR-ROM page at $0C00.
 * $A000-$A7FF    Select a 1kB CHR-ROM page at $1000.
 * $A800-$AFFF    Select a 1kB CHR-ROM page at $1400.
 * $B000-$B7FF    Select a 1kB CHR-ROM page at $1800.
 * $B800-$BFFF    Select a 1kB CHR-ROM page at $1C00.
 */
	andi	t0, a0, 0x3800
	srl	t0, t0, 11 - 1
	addu	t0, t0, WORK
	sh	a1, OFFSET_CHRBANK_REG0000(t0)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	GLOBAL(store13_c000)
store13_c000:
/* Namcot 106
 * c000-c7ff,c800-cfff,d000-d7ff,d800-dfff が
 * PPU addr 2000-,2400-,2800-,2c00- の chrbank.
 * つまり VROM なんだけど ...
 * ここで VROM は面倒いのでやらない。
 * 書き込む値が 0xe0 以上の時は VRAM に接続するようだ。
 * c000=e0,d000=e0,c800=e1,d800=e1 (n_clasic.nes) のとき
 * c000- と d000- が同じ なので 横持ちである。
 * しかも n_clasic.nes は PPU 読み出ししているせいか、ここを
 * 実装しないと暴走する。
 * c000,c800,d000,d800 へ書き込む値が
 * e0,  e1,  e0,  e1   -> 横持ち
 * e0,  e0,  e1,  e1   -> 縦持ち
 * e0,  e0,  e0,  e0   -> 1 画面 2000-
 * e1,  e1,  e1,  e1   ->        2400-
 * 他の組み合わせの時は どうする?
 * c800-,d000- の組み合わせだけで 決めちゃおう。
 * ss_pbank0,1 を使う。
 * fcirc91.nes は これでいけるかも。(初期値 横持ち)
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("13(%x->%x)\n");
#endif
	andi	t0, a0, 0x1800
	srl	t0, t0, 11  /* 0x1800 -> 3 */
	addu	t0, t0, WORK
	andi	t1, a1, 0x01
	sb	t1, OFFSET_SS_PBANK0(t0)

	lbu	t1, OFFSET_SS_PBANK0 + 1(WORK)
	lbu	t2, OFFSET_SS_PBANK0 + 2(WORK)
	beq	t1, t2, 2f  /* 1 画面 */
	mul	t2, t2, 0x400  /* t2 が 0 なら 横持ち */
	addiu	t2, t2, 0x400
	sh	t2, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	sh	zero, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	jr	STORE_DONE
2:
	mul	t1, t1, 0x400
	addiu	t1, t1, 0x400
	sh	t1, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	sh	zero, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	jr	STORE_DONE

	
	GLOBAL(store13_e000)
store13_e000:
/* Namcot 106
 * e000-e7ff: pbank 8000-9fff
 * e800-efff:       a000-bfff
 * f000-f7ff:       c000-dfff
 * f800: wram address register
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("13(%x->%x)\n");
#endif
	lw	t2, OFFSET_PRGROM(WORK)
	lbu	t4, OFFSET_PBANK_MASK(WORK)
	andi	t0, a0, 0x1800
	xori	t1, t0, 0x1800
	beq	t1, zero, 8f
/* t0: 0 or 0x800 or 0x1000
 * t1: (0 or 0x2000 or 0x4000) - 0x8000
 * t2: prgrom
 * t3: 0 or 4 or 8
 * t4: bank * 0x2000
 */
	and	t4, t4, a1
	mul	t4, t4, 0x2000
	srl	t3, t0, 11 - 2
	sll	t1, t0, 2
	subu	t1, t2, t1
	addiu	t1, t1, -0x8000
	addu	t1, t1, t4
	addu	t3, t3, WORK

#if 0
/* debug */
	subu	a1, t1, t2
	subu	a2, t3, WORK
	move	a3, t1
	PRINTF("13(%x,%x,%x)\n");
#endif
	sw	t1, OFFSET_PAGE_TABLE + 0x10(t3)
	jr	STORE_DONE

8:
	jr	STORE_DONE

	GLOBAL(store16_8000)
store16_8000:
/* VRC2-Shift
 * 8000-8fff: pbank at 8000-9fff
 * 9000-9fff: mirror d1,d0
 *                    00=横持ち
 *                    01=縦持ち
 *                    10=one screen 2000- ?
 *                    11            2400- ?
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("16(%x->%x)\n");
#endif
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	andi	t0, a0, 0x1000
	bne	t0, zero, store16_9000
	lw	t0, OFFSET_PRGROM(WORK)
	and	t2, a1, t1
	mul	t2, t2, 0x2000
	addu	t0, t0, t2
	addiu	t0, t0, -0x8000
	sw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	jr	STORE_DONE

	GLOBAL(store16_9000)
store16_9000:
/* mapper 0x19(VRC2/4 Even), mapper 0x44(Sunsoft 4)
 * からも来る
 * d1=1 なら one screen, d0 によって 2000- or 2400-
 * d1=0 なら d0 によって 横持ち or 縦持ち
 */
	andi	t0, a1, 0x02
	andi	t1, a1, 0x01
	mul	t1, t1, 0x400
	bne	t0, zero, 2f
	sh	zero, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	addiu	t1, t1, 0x400
	sh	t1, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
#if 0
/* debug */
	move	a2, t1
	PRINTF("16_mirror(%x,%x)\n");
#endif
	jr	STORE_DONE
2:
/* one screen */
	sh	zero, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	sh	t1, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	jr	STORE_DONE

	GLOBAL(store16_a000)
store16_a000:
/* a000-afff: pbank at a000-bfff
 * b000,b002,...,bffe: chrbank 0000-03ff
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("16(%x->%x)\n");
#endif
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	andi	t0, a0, 0x1000
	bne	t0, zero, store16_b000
	GLOBAL(store16_a000_common)
store16_a000_common:
/* store19_a000, store17_a000 からも来る。
 * t1: pbank mask
 */
	lw	t0, OFFSET_PRGROM(WORK)
	and	t2, a1, t1
	addiu	t2, t2, -5  /* 後の -0xa000 に相当 */
	mul	t2, t2, 0x2000
	addu	t0, t0, t2
	sw	t0, OFFSET_PAGE_TABLE + 0x14(WORK)
	jr	STORE_DONE

	GLOBAL(store16_b000)
store16_b000:
/* b000,b001,c000,c001,d000,d001,e000,e001:
 * chrbank 0000-,0400-,..., 1c00-
 * (addr >> 10) が 0x2c,0x30,0x34,0x38 になるようにする。
 *              -> 0x0c,0x10,0x14,0x18 へ。
 * (addr & 1) -> 0 or 2
 * Shift this value right one bit
 * ということだ。
 * konami/twinbee3.nes では
 * b000,b002; b001,b003; c000,c002; c001,c003; ... のように書き込んでいて
 * b000 の 上位 4bits が b002 に書き込まれているようだ。
 * そこで、 address & 0x02 が 0 でなければ無視する。
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("16(%x->%x)\n");
#endif
	srl	t0, a0, 10
	andi	t1, a0, 0x02
	bne	t1, zero, store_done
	andi	t0, t0, 0x1c
	andi	t1, a0, 0x0001
	sll	t1, t1, 1
	or	t0, t0, t1
	addu	t0, t0, WORK
	srl	t1, a1, 1
	sh	t1, OFFSET_CHRBANK_REG0000 - 0x0c(t0)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	GLOBAL(store17_a000)
store17_a000:
/* a000-afff: pbank at a000-bfff
 * b000,b001,b002,b003,...,bfff: chrbank 0000-03ff,0400-07ff
 * でも バンクレジスタのアドレス割り付けが違うので mapper 0x16 とは別。
 */
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	andi	t0, a0, 0x1000
	beq	t0, zero, store16_a000_common

	GLOBAL(store17_b000)
store17_b000:
/* b000,b001: chr @ 0000- low, high
 * b002,b003:       0400-
 * c000,c001:       0800- low, high
 * c002,c003:       0c00-
 * d000,d001:       1000-
 * d002,d003:       1400-
 * e000,e001:       1800-
 * e002,e003:       1c00-
 * parodius.nes, kdracula.nes, crisis_f.nes は
 * b000,b004, b008,b00c, のタイプ。
 *
 * address & (0x01 | 0x04) によって low or high
 * address & (0x02 | 0x08) によって 0000-, 0400-
 *                                  +0     +2
 * address >> 10 が 0x2c,0x30,0x34,0x38
 *               -> 0x0c,0x10,0x14,0x18
 * t3: previous chrbank
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("17_chrbank(%x->%x)\n");
#endif
	srl	t0, a0, 10
	andi	t0, t0, 0x1c
	andi	t1, a0, 0x0002 | 0x0008
	beq	t1, zero, 2f
	addiu	t0, t0, 0x02
2:
	addu	t0, t0, WORK
	lhu	t3, OFFSET_CHRBANK_REG0000 - 0x0c(t0)
	andi	t1, a0, 0x0001 | 0x0004
	bne	t1, zero, store19_b000_common_high
	j	store19_b000_common_low

	GLOBAL(store17_e000)
store17_e000:
/* e000-efff: chrbank
 * f000-ffff: irq
 */
	andi	t0, a0, 0x0f
	andi	t1, a0, 0x1000
	beq	t1, zero, store17_b000
#if 0
/* debug */
	move	a2, a0
	PRINTF("17_irq(%x->%x)\n");
#endif
/* irq
 * parodius.nes は
 * f000,f004: count L,H
 * f008 が trigger
 * t0: addr & 0x0f
 */
	.set	noreorder
	beq	t0, zero, store19_f000_0
	andi	t1, t0, 0x04
	bne	t1, zero, store19_f002
	andi	t1, t0, 0x08
	bne	t1, zero, store19_f001
	nop
	.set	reorder
	jr	STORE_DONE

	
	GLOBAL(store18_8000)
store18_8000:
/* VRC6
 * 8000-ffff: pbank @ 8000-bfff - bank no との対応は ?
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("18(%x->%x)\n");
#endif
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	andi	t0, a0, 0x1000
	bne	t0, zero, store18_9000  /* sound */
/* 偶数のみ書き込まれると仮定しよう。
 * 違った。奇数が書き込まれている。
 */
	lw	t0, OFFSET_PRGROM(WORK)
	sll	t2, a1, 1
	and	t1, t1, t2
	mul	t1, t1, 0x2000
	addiu	t1, t1, -0x8000
	addu	t0, t0, t1
	sw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x14(WORK)
	jr	STORE_DONE

	GLOBAL(store18_9000)
store18_9000:
/* VRC6 sound
 *
 * 9000,a000,b000 ch1,ch2,ch3 Sound Control 1
 * 9001,a001,b001 ch1,ch2,ch3 Sound Control 2
 * 9002,a002,b002 ch1,ch2,ch3 Sound Control 3
 *
 * address & 0x3000 -> 1000,2000,3000
 *              nes の 5000,5004,5008
 * address & 0x0001 -> 5003,5007,500b
 * address & 0x0002 -> 5002,5006,500a
 * にしてみる。
 * 
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("VRC6_SoundWrite(%x->%x)\n");
#endif
	lbu	t4, OFFSET_VRC6_REG_SWAP(WORK)
	andi	t0, a0, 0x3000
	addiu	t0, t0, -0x1000
	srl	t0, t0, 12 - 2  /* 0x2000 -> 0x08 */
	andi	t1, a0, 0x0003
	beq	t1, zero, 1f
	xor	t1, t1, t4
	addu	t0, t0, t1
	addiu	t0, t0, 1
	xori	t3, t1, 0x0003
	beq	t3, zero, store_done
1:
	ori	a0, t0, 0x5000
#if 0
	andi	t0, a0, 0x08
	beq	t0, zero, store_done
#endif
#if 0
/* debug */
	move	a2, a0
	PRINTF("18_sound(%x->%x)\n");
#endif
	
	lw	a2, OFFSET_SOUNDCYCLE(WORK)
	lbu	t3, OFFSET_SOUND_CYCLE_DIVIDER(WORK)
	divu	zero, a2, t3
	mflo	a2
	sw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	sw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	sw	gp, OFFSET_THREAD_GP(WORK)
	
	jal	Sound_Write

#if 0
/* debug */
	PRINTF("written\n");
#endif
	
	lw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	lw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	lw	gp, OFFSET_THREAD_GP(WORK)
	jr	STORE_DONE
	
	
	GLOBAL(store18_a000)
store18_a000:
/* VRC6
 * b003 以外 sound
 * b003 Mirror Setting
 * Bmmmmmmm
 * B: SRAM enable/disable 1:enable 0:disable
 * mmmmmmm: Mirroring
 *  #08 HHHH
 *  #20 VHVH
 *  #24 VVHH
 *  #28 VVVV
 *  #2C HHHH
 */
#if 0
/* debug */
	move	a2, a0
	lhu	a3, OFFSET_LINECNT(WORK)
	PRINTF("18(%x->%x)line=%x\n");
#endif

	xori	t0, a0, 0xb003
	bne	t0, zero, store18_9000
#if 0
/* debug */
	lhu	a2, OFFSET_LINECNT(WORK)
	PRINTF("18_mirror=%x at line=%x\n");
#endif
/* mirror
 * 0x2c の位について
 * 20=横持ち
 * 24=縦持ち
 * 28=one screen @ 2000- とちゃう ?
 * 2c=one screen @ 2400- とちゃう ? (cv3j.nes)
 * 他は one screen @ 2000-
 * 0x08 の位が立っていたら one screen
 * 後は 0x04 の位で見る。
 */
	
	andi	t0, a1, 0x08
	andi	t1, a1, 0x04
	bne	t0, zero, 8f
/* 横持ちなら t1=0
 * 縦持ちなら    4
 */
	sll	t1, t1, 8  /* 0x04 -> 0x400 */
	addiu	t1, t1, 0x400
	sh	t1, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	sh	zero, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	jr	STORE_DONE
8:
/* one screen
 * 0x2c なら 2400- としておこう。
 * esperdr2.nes が 0xac を書き込んでいる。
 */
	andi	t0, a1, 0x2c
	xori	t0, t0, 0x2c
	li	t1, 0x400
	beq	t0, zero, 1f
	li	t1, 0
1:
	sh	t1, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	sh	zero, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	jr	STORE_DONE
	
	GLOBAL(store18_c000)
store18_c000:
/* c000-cfff: pbank @ c000-dfff
 * d000,d001,d002,d003,e000,e001,e002,e003:
 * chrbank 0000-,0400-,..., 1c00-
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("18(%x->%x)\n");
#endif
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	andi	t0, a0, 0x1000
	bne	t0, zero, 10f
	lw	t0, OFFSET_PRGROM(WORK)
	and	t1, t1, a1
	addiu	t1, t1, -6  /* 後の -0xc000 に相当 */
	mul	t1, t1, 0x2000
	addu	t0, t0, t1
	sw	t0, OFFSET_PAGE_TABLE + 0x18(WORK)
	jr	STORE_DONE
10:
/* chrbank
 * addr & 0x1000 -> 0x1000 なら d000-d003 の書き込み。(+0)
 *                  0           e000-e003             (+0x8)
 * t0: addr & 0x1000
 * 下からも来る。
 */
#if 0
/* debug */
	move	a2, a0
	lhu	a3, OFFSET_LINECNT(WORK)
	PRINTF("18_chrbank(%x->%x)line=%x\n");
#endif
	lbu	t2, OFFSET_VRC6_REG_SWAP(WORK)
	andi	t1, a0, 0x0003
	beq	t1, zero, 2f
	xori	t3, t1, 0x0003
	beq	t3, zero, 2f
	xor	t1, t1, t2
2:
	sll	t1, t1, 1
	bne	t0, zero, 1f
	addiu	t1, t1, 8
1:
	addu	t1, t1, WORK
	sh	a1, OFFSET_CHRBANK_REG0000(t1)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	GLOBAL(store18_e000)
store18_e000:
/* e000-efff: chrbank
 * t0: addr & 0x1000
 * f000-ffff: irq
 * f000 IRQ scanline(本当はclock) counter
 *  (#$100-i)*#$72 clock wait.
 * f001 IRQ enable/disable
 * f002 IRQ enable/disable
 *   ------AB
 *   A 0:IRQ disable
 *     1:IRQ enable
 *   B 0:f001 is IRQ disable
 *     1:f001 is IRQ enable
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("18(%x->%x)\n");
#endif

	andi	t1, a0, 0x03
	andi	t0, a0, 0x1000
	beq	t0, zero, 10b

#if 0
/* debug */
	move	a2, a0
	lhu	a3, OFFSET_LINECNT(WORK)
 	PRINTF("18_irq(%x->%x) line=%x\n");
#endif

#if 0
/* old code */
	.set	noreorder
	beq	t1, zero, 1f   /* f000 */
	andi	t1, a0, 0x02
	bne	t1, zero, 2f   /* f002 */
	nop
	.set	reorder
/* f001
 * IRQ enable/disable
 * f002 の d0 によって IRQ enabled/disabled が 反転 ?
 * ちがう。
 * f002 の d0 が 0 or 初期状態なら ここ f001 への書き込み の d1 が enable.
 */
	lbu	t0, OFFSET_VRC6_IRQ_SELECT(WORK)
	andi	t0, t0, 0x01
	beq	t0, zero, 3f
/* f002 の d0 が 1 なら f002 の d1 が enable ? */
	jr	STORE_DONE

1:
/* f000
 * IRQ count
 */
	sb	a1, OFFSET_VRC6_IRQ(WORK)
	jr	STORE_DONE
2:
/* f002
 * d1: 1=IRQ enable,0=disable
 * d0: 何だろう ...
 */
	sb	a1, OFFSET_VRC6_IRQ_SELECT(WORK)
3:
/* 上からも来る。*/
	andi	t0, a1, 0x02
	sb	t0, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	lbu	t1, OFFSET_VRC6_IRQ(WORK)
	lh	t2, OFFSET_LINECNT(WORK)
	li	t3, 0x100
	subu	t1, t3, t1
	addu	t2, t2, t1
	sh	t2, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE
#endif

#if 1
/* new code
 *  Any value written here will
 *  reset the IRQ counter to zero.
 * ということで、
 * f002 への書き込みは IRQ 禁止してみる。
 */
	.set	noreorder
	beq	t1, zero, 1f   /* f000 */
	lbu	t3, OFFSET_VRC6_IRQ_REG(WORK)
	and	t1, a0, t3
	bne	t1, zero, 2f   /* f001 */
	nop
	.set	reorder
/* f002
 * IRQ restart ?
 */
	lbu	t0, OFFSET_VRC6_IRQ_SELECT(WORK)
	andi	t0, t0, 0x01
	bne	t0, zero, 22f
	li	t1, 0x8000
	sh	t1, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE
2:
/* f001 */
	sb	a1, OFFSET_VRC6_IRQ_SELECT(WORK)
	andi	t0, a1, 0x02
22:
#if 0
/* debug */
	move	a2, t0
	PRINTF("xxx,%x,%x,");
#endif
	sb	t0, OFFSET_LINECNT_IRQ_ENABLED(WORK)
3:
/* 下からも来る のか? */
	lbu	t1, OFFSET_VRC6_IRQ(WORK)
	lh	t2, OFFSET_LINECNT(WORK)
	li	t3, 0x100
	subu	t1, t3, t1
	addu	t2, t2, t1
#if 0
/* debug */
	move	a2, t2
	PRINTF("%x,next=%x\n");
#endif
	sh	t2, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE
1:
/* f000 */
	sb	a1, OFFSET_VRC6_IRQ(WORK)
	jr	STORE_DONE
#endif
	
	GLOBAL(store19_8000)
store19_8000:
/* VRC2/4 Even
 * 8000-8fff: pbank @ 8000-9fff or c000-dfff (map mode による)
 * 9000: mirror (mapper 0x16 と同じ)
 * 9002: d0: SRAM enable, d1: map mode 0=8000-9fff,1=c000-dfff
 */
	lbu	t1, OFFSET_VRC4_MAPMODE(WORK)
	andi	t0, a0, 0x1000
	bne	t0, zero, 9f
/* pbank at ... 8000-? c000-? */
	lw	t0, OFFSET_PRGROM(WORK)
	lbu	t2, OFFSET_PBANK_MASK(WORK)
	and	t2, t2, a1
	sb	t2, OFFSET_VRC4_PBANK(WORK)
	addiu	t2, t2, -4  /* 後の -0x8000 に相当 */
	mul	t2, t2, 0x2000
	addu	t0, t0, t2
	andi	t1, t1, 0x02
	bne	t1, zero, 2f
8:
/* 8000-9fff
 * 下からも来る。
 */
	sw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	jr	STORE_DONE
	
9:
/* 9000 なら mirror
 * 9002 なら mapmode
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("19_9000(%x->%x)\n");
#endif
	lbu	t1, OFFSET_VRC4_MAPMODE(WORK)
	andi	t0, a0, 0x0002
	beq	t0, zero, store16_9000
	lbu	t2, OFFSET_VRC4_PBANK(WORK)
	sb	a1, OFFSET_VRC4_MAPMODE(WORK)
	xor	t1, t1, a1
	andi	t1, t1, 0x02
	beq	t1, zero, store_done
/* mapmode が 変わった。*/
	lw	t0, OFFSET_PRGROM(WORK)
	addiu	t2, t2, -4  /* 後の -0x8000 に相当 */
	mul	t2, t2, 0x2000
	addu	t0, t0, t2
	andi	t1, a1, 0x02
	beq	t1, zero, 8b
2:
/* c000-dfff */
	addiu	t0, t0, -0x4000  /* すでに -0x8000 されている */
	sw	t0, OFFSET_PAGE_TABLE + 0x18(WORK)
	jr	STORE_DONE

	GLOBAL(store19_a000)
store19_a000:
/* VRC2/4 Even
 * a000-afff: pbank @ a000-bfff
 */
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	andi	t0, a0, 0x1000
	beq	t0, zero, store16_a000_common

	GLOBAL(store19_b000)
store19_b000:
/* chrbank は low, high 4bits ずつの書き込みで d3-d0 が有効。
 * b000: chrbank @ 0000-03ff low
 * b002:                     high
 * b001/b004:      0400-07ff low
 * b003/b006:                high
 * 同様に c000,c002: 0800-0bff, c001/c004,c003/c006: 0c00-0fff,
 * d000,d002: 1000-13ff, d001/d004,d003/d006: 1400-17ff
 * e000,e002: 1800-1bff, e001/e004,e003/e006: 1c00-1bff
 * goemong2.nes は
 * b000,b040, b080,b0c0, のタイプ。
 *
 * t1: addr & (0x02 | 0x40) が low or high.
 * t2: addr & (0x01 | 0x04 | 0x80) が 0 かどうかで 0000-, 0400-.
 *                                              -> +0     +2
 * t0: (addr >> 10) が 0x2c,0x30,0x34,0x38 になるようにする。
 *                  -> 0x0c,0x10,0x14,0x18 へ。
 * t3: previous chrbank
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("19_chrbank(%x->%x)\n");
#endif
	srl	t0, a0, 10
	andi	t0, t0, 0x1c
	addu	t0, t0, WORK
	andi	t2, a0, 0x0001 | 0x0004 | 0x0080
	beq	t2, zero, 1f
	addiu	t0, t0, 2
1:
	lhu	t3, OFFSET_CHRBANK_REG0000 - 0x0c(t0)
	andi	t1, a0, 0x0002 | 0x0040
	beq	t1, zero, 2f
	GLOBAL(store19_b000_common_high)
store19_b000_common_high:
/* higher bit の設定. store17_b000 からも来る */
	sll	t1, a1, 4
	andi	t3, t3, 0x0f
3:
/* 下からも来る。*/
	or	t1, t1, t3
	andi	t1, t1, 0xff
	sh	t1, OFFSET_CHRBANK_REG0000 - 0x0c(t0)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE
	GLOBAL(store19_b000_common_low)
store19_b000_common_low:
2:
/* lower bit の設定 */
	andi	t1, a1, 0x0f
	andi	t3, t3, 0xf0
	j	3b

	GLOBAL(store19_e000)
store19_e000:
/* e000-efff: chrbank
 * f000,f002: d3-d0: LINECNT_IRQ (ブランキング分も含む?) low, high
 * f001,f004: d2: IRQ enable
 * f003,f006: timer start?
 */
	andi	t0, a0, 0x1000
	beq	t0, zero, store19_b000

	GLOBAL(store19_f000)
store19_f000:
#if 0
/* debug */
	move	a2, a0
	PRINTF("19(%x->%x)\n");
#endif
/* f000: IRQ counter low
 * f002, f040:             high
 * f001, f080: d1: IRQ disabled/enabled
 *             d0: 1=timer restart ?
 * goemong2.nes が f000,f040,f080 を使ってる。
 */
	.set	noreorder
	andi	t0, a0, 0x07 | 0x40 | 0x80
	beq	t0, zero, store19_f000_0
	xori	t1, t0, 0x02
	beq	t1, zero, store19_f002
	xori	t1, t0, 0x01
	beq	t1, zero, store19_f001
	xori	t1, t0, 0x04
	beq	t1, zero, store19_f001
	xori	t1, t0, 0x03
	beq	t1, zero, store19_f003
	andi	t1, t0, 0x40
	bne	t1, zero, store19_f002
	andi	t1, t0, 0x80
	bne	t1, zero, store19_f001
	nop
	.set	reorder

#if 0
/* debug */
	move	a2, a0
	PRINTF("19_irq(%x->%x)\n");
#endif
	
	jr	STORE_DONE

	GLOBAL(store19_f000_0)
store19_f000_0:
/* irq counter low
 * store17_e000 からも来る。
 */
	andi	t1, a1, 0x0f
	sb	t1, OFFSET_VRC4_IRQ_L(WORK)
	jr	STORE_DONE

	GLOBAL(store19_f002)
store19_f002:
/* irq counter high
 * store17_e000 からも来る。
 */
	sll	t1, a1, 4
	sb	t1, OFFSET_VRC4_IRQ_H(WORK)
	jr	STORE_DONE

	GLOBAL(store19_f001)
store19_f001:
/* d1=1: irq enabled
 * d0=1: timer start ?
 * gradius2.nes では f001 には 3 を、
 *                   f003 には 0xa を書いている。
 * f001 or f003 の、d1 は共通として
 *                  d0 or d3 =1: timer start にしてみる。
 * 違うなあ.
 * d0を無視して強制的に timer start とすると romupa.nes バッチリ。
 *                                           gradisu2.nes も。
 *                                           kworld2.nes も !
 * mini4ku.nes も 前よりはよくなった (デモがおかしいが ... chrbank?)
 *             イメージ不良ちゃう ?
 * 
 * store17_e000 からも来る。
 */
	andi	t0, a1, 0x02
	sb	t0, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	andi	t1, a1, 0x01 | 0x08
	j	1f
	bne	t1, zero, 1f
/* timer stop */
	li	t0, 0x8000
	sh	t0, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE
1:
/* timer start
 * 0x100 - (irq_l | irq_h) を linecnt に加えてみる。
 *                            -> gradius2.nes タイトル、ゲーム画面共に OK!
 * 途中のプロミネンスで、画面の真ん中 1 line がちらつく。0x101 でも。
 *   -> linecnt_over19 で対策した。
 * 0x101 - にしたほうが goemong2.nes, crisis_f.nes 良くなった。
 */
	lbu	t0, OFFSET_VRC4_IRQ_L(WORK)
	lbu	t1, OFFSET_VRC4_IRQ_H(WORK)
	lh	t3, OFFSET_LINECNT(WORK)
	sh	t3, OFFSET_VRC4_LINECNT_SAVE(WORK)
	or	t0, t0, t1
	li	t2, 0x101
	subu	t0, t2, t0
	addu	t3, t3, t0
	sh	t3, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE

	GLOBAL(store19_f003)
store19_f003:
/* 前回の f001 書き込み時の linecnt を使って
 * timer restart ?
 */
	lbu	t0, OFFSET_VRC4_IRQ_L(WORK)
	lbu	t1, OFFSET_VRC4_IRQ_H(WORK)
	lh	t3, OFFSET_VRC4_LINECNT_SAVE(WORK)
	or	t0, t0, t1
	addu	t3, t3, t0
	sh	t3, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE

	GLOBAL(store20_8000)
store20_8000:
/* 8fff: pbank @ 8000- or c000-
 */
	lbu	t2, OFFSET_SS_PBANK0 + 1(WORK)
	andi	t0, a0, 0x1000
	bne	t0, zero, 10f
	lw	t3, OFFSET_PRGROM(WORK)
	andi	t2, t2, 0x02
	sll	t2, t2, 2      /* 0x02 -> 0x08 */
	addiu	t4, t2, 0x10   /* 後の -0x8000 に相当 */
	sll	t4, t4, 3 + 8  /* 0x08 -> 0x4000 */
	addu	t2, t2, WORK
	subu	t3, t3, t4
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	and	t1, t1, a1
	sb	t1, OFFSET_SS_PBANK0(WORK)
	mul	t1, t1, 0x2000
	addu	t3, t3, t1
	sw	t3, OFFSET_PAGE_TABLE + 0x10(t2)
	jr	STORE_DONE
10:
/* 9fff: mirror */
	andi	t0, a1, 0x01
	mul	t0, t0, 0x400
	addiu	t0, t0, 0x400
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	
	xor	t1, t2, a1
	andi	t1, t1, 0x02
	beq	t1, zero, store_done

/* d1 が mapmode. 8000- or c000- */
	lw	t0, OFFSET_PRGROM(WORK)
	lbu	t2, OFFSET_SS_PBANK0(WORK)
	sb	a1, OFFSET_SS_PBANK0 + 1(WORK)
	andi	t1, a1, 0x02
	bne	t1, zero, 2f
/* 8000- */
	addiu	t2, t2, -0x04  /* 後の -0x8000 に相当 */
	addiu	t3, WORK, 0
	j	1f
2:
/* c000- */
	addiu	t2, t2, -0x06  /* 後の -0xc000 に相当 */
	addiu	t3, WORK, 8
1:
	mul	t2, t2, 0x2000
	addu	t2, t2, t0
	sw	t2, OFFSET_PAGE_TABLE + 0x10(t3)
	jr	STORE_DONE

	GLOBAL(store20_a000)
store20_a000:
/* afff: pbank @ a000-
 */
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	andi	t0, a0, 0x1000
	bne	t0, zero, 10f
	lw	t2, OFFSET_PRGROM(WORK)
	and	t1, t1, a1
	addiu	t1, t1, -5   /* 後の -0xa000 に相当 */
	mul	t1, t1, 0x2000
	addu	t1, t1, t2
	sw	t1, OFFSET_PAGE_TABLE + 0x14(WORK)
	jr	STORE_DONE
10:
/* bff0-bff7: chrbank @ 0000-,0400-,...,1c00-
 */
	andi	t0, a0, 0x07
	sll	t0, t0, 1
	addu	t0, t0, WORK
	sh	a1, OFFSET_CHRBANK_REG0000(t0)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE
	
	
	GLOBAL(store21_8000)
store21_8000:
/* Taito TC0190
 * 8000: pbank @ 8000-9fff
 * 8001: pbakk @ a000-bfff
 * 8002: chrbank 0000-07ff
 * 8003: chrbank 0800-0fff
 */
	andi	t1, a0, 0x0002
	andi	t0, a0, 0x0001
	bne	t1, zero, 2f
/* pbank */
	lbu	t2, OFFSET_PBANK_MASK(WORK)
	lw	t3, OFFSET_PRGROM(WORK)
	sll	t1, t0, 2
	addu	t1, t1, WORK
	addiu	t0, t0, 4  /* 後の -0x8000 に相当 */
	and	t2, t2, a1
	subu	t2, t2, t0
	mul	t2, t2, 0x2000
	addu	t2, t2, t3
	sw	t2, OFFSET_PAGE_TABLE + 0x10(t1)
	jr	STORE_DONE
2:
#if 0
/* debug */
	move	a2, a0
	PRINTF("21(%x->%x)\n");
#endif
	sll	t0, t0, 2
	addu	t0, t0, WORK
	sll	t1, a1, 1
	sh	t1, OFFSET_CHRBANK_REG0000(t0)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG0400(t0)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	GLOBAL(store21_a000)
store21_a000:
/* a000-a003: chrbank @ 1000-,1400-,1800-,1c00-
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("21(%x->%x)\n");
#endif
	andi	t0, a0, 0x0003
	sll	t0, t0, 1
	addu	t0, t0, WORK
	sh	a1, OFFSET_CHRBANK_REG1000(t0)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	GLOBAL(store21_c000)
store21_c000:
/* c000: irq counter - カウントアップして オーバーフローで割り込み
 * c001,c002: enable/disable
 * とりあえず c000 に 0 でない値なら IRQ enable.
 */
#if 0
	move	a2, a0
	PRINTF("21_irq(%x->%x)\n");
#endif
	lh	t0, OFFSET_LINECNT(WORK)
	sb	a1, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	li	t1, 0x100
	subu	t1, t1, a1
	addu	t0, t0, t1
	sh	t0, OFFSET_LINECNT_IRQ(WORK)
	
	jr	STORE_DONE
	
	GLOBAL(store42_8000)
store42_8000:
/* GNROM
 * xxPPCCCC
 * PP: pbank @ 8000-ffff
 * CC: cbank @ 0000-1fff
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("42(%x->%x)\n");
#endif
	lhu	t2, OFFSET_CHRBANK_REG0000(WORK)
	andi	t3, a1, 0x0f
	sll	t3, t3, 3    /* 0x400 bytes/bank -> 0x2000 bytes/bank */
	beq	t2, t3, 10f
	move	t0, WORK
	addiu	t1, t0, 0x10
1:
	sh	t3, OFFSET_CHRBANK_REG0000(t0)
	addiu	t3, t3, 1
	addiu	t0, t0, 2
	bne	t0, t1, 1b

	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)

10:	
	
	andi	t0, a1, 0x30
	lw	t1, OFFSET_PRGROM(WORK)
	sll	t0, t0, 15 - 4  /* 0x30 -> 0x18000 */
	addiu	t0, t0, -0x8000
	addu	t0, t0, t1
	sw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x1c(WORK)

	jr	STORE_DONE

	GLOBAL(store43_c000)
store43_c000:
/* Sunsoft 3
 * cfff: IRQ counter
 * dfff: d4=1 で IRQ on
 */
#if 0
/* debug */
	move	a2, a0
	lhu	a3, OFFSET_LINECNT(WORK)
	PRINTF("43_irq(%x->%x)line=%x\n");
#endif

	andi	t0, a0, 0x1000
	bne	t0, zero, 10f
/* なんか、cfff に 16, a8 と書き込んで から trigger をかけている。
 * 0xdb - value で。
 * 0xa8 -> 0xdb - 0xa8 -> 0x33 になるように。
 */
	lh	t1, OFFSET_LINECNT(WORK)
	li	t2, 0xdb
	subu	t2, t2, a1
	addu	t1, t1, t2
	sh	t1, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE

10:
	andi	t0, a1, 0x10
	sb	t0, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	jr	STORE_DONE


	GLOBAL(store44_8000)
store44_8000:
/* Sunsoft 4
 * 8000-8fff: PPU 0000-07ff
 * 9000-9fff: PPU 0800-0fff
 * a000-afff: PPU 1000-17ff
 * b000-bfff: PPU 1800-1fff
 * address & 0x3000 -> 0,4,8,c になるように。
 * chrbank no を << 1 する。
 */
	lw	t0, OFFSET_CHR_OFFSET_MASK(WORK)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	mul	t1, a1, 0x400 * 2
	and	t1, t1, t0
	srl	t1, t1, 10  /* / 0x400 */
	andi	t2, a0, 0x3000
	srl	t2, t2, 12 - 2  /* 0x3000 -> 0x0c */
	addu	t2, t2, WORK
	sh	t1, OFFSET_CHRBANK_REG0000(t2)
	addiu	t1, t1, 1
	sh	t1, OFFSET_CHRBANK_REG0400(t2)
	jr	STORE_DONE

	GLOBAL(store44_c000)
store44_c000:
/* Sunsoft 4
 * aftrbrn2.nes が screen data を chrrom に持って bank 切り替えしている。
 * C000: CHR page_number at NameTable(VRAM_A10=L) (page_size=index_size=1k)
 * D000: CHR page_number at NameTable(VRAM_A10=H) (page_size=index_size=1k)
 *
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("44_c000(%x->%x)\n");
#endif
	andi	t0, a0, 0x1000
	srl	t0, t0, 12     /* 0x1000 -> 0x01 */
	addu	t0, t0, WORK
	sb	a1, OFFSET_SUN4_VROM0(t0)
	jr	STORE_DONE
	
	GLOBAL(store44_e000)
store44_e000:
/* 
 * e000-efff: VRAM mirror d1,d0=00: 横持ち 01: 縦持ち 10: 2000-, 11: 2400- 1 画面
 *            d4: 1=VRAMの代わりに VROM を使う
 * (store16_9000 を使う)
 * f000-ffff: d3-d0?: prg rom @ 8000-bfff
 * (nantbase.nes は 0x10 の bit が SRAM enable)
 * prgbank << 1 する。
 */
#if 0
/* debug */
	andi	t0, a0, 0x1000
	bne	t0, zero, 1f
	move	a2, a0
	PRINTF("44_mirror(%x->%x)\n");
1:
#endif
	lbu	t1, OFFSET_PBANK_MASK(WORK)
	andi	t0, a0, 0x1000
	bne	t0, zero, 1f
	andi	t0, a1, 0x10
	sll	t0, t0, 2     /* 0x10 -> 0x40, VROM を使う */
	sb	t0, OFFSET_SUN4_VROM_ENABLED(WORK)
	j	store16_9000
1:
	lw	t0, OFFSET_PRGROM(WORK)
	sll	t2, a1, 1
	and	t2, t2, t1
	mul	t2, t2, 0x2000
	addiu	t2, t2, -0x8000
	addu	t2, t2, t0
	sw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x14(WORK)
	jr	STORE_DONE
	
	GLOBAL(store45_8000)
store45_8000:
/* Sunsoft 5
 * 8000 = command #
 */
#if 0
/* debug */
	move	a2, PC
	PRINTF("SUN5 CMD=%x at PC=%x\n")
#endif
	sb	a1, OFFSET_SUN5_CMD(WORK)
	jr	STORE_DONE

	GLOBAL(store45_a000)
store45_a000:
#if 0
/* debug */
	move	a2, PC
	PRINTF("SUN5 ARG=%x at PC=%x\n")
#endif
/* Sunsoft 5
 * a000 = argument
 *  #0 : CHR page_number at $0000-$03FF
 *  #1 : CHR page_number at $0400-$07FF
 *  #2 : CHR page_number at $0800-$0bFF
 *  #3 : CHR page_number at $0c00-$0fFF
 *  #4 : CHR page_number at $1000-$13FF
 *  #5 : CHR page_number at $1400-$17FF
 *  #6 : CHR page_number at $1800-$1bFF
 *  #7 : CHR page_number at $1c00-$13FF
 *  #8 : PRG page_number at $6000-$7FFF
 *  #9 : PRG page_number at $8000-$9FFF
 *  #A : PRG page_number at $A000-$BFFF
 *  #B : PRG page_number at $C000-$DFFF
 *  #C : Mirror
 *     bit ------MM
 *     MM 0: VHVH
 *        1: VVHH
 *        2: VVVV
 *        3: HHHH
 *  #D : IRQ enable/disable
 *    #00: disable
 *    #81: enable
 *  #E : IRQ clock Lower 1 byte
 *  #F : IRQ clock Higher 1 byte
 *
 * 考察 IRQは1ワードの待機クロック数を入力する
 * 0を入力すると、次のクロックでIRQが発生する。
 * iを入力すると、i+1クロック後にIRQが発生する。
 */
	lbu	t0, OFFSET_SUN5_CMD(WORK)
	andi	t1, t0, 0x07
	addiu	t3, t0, -0x0c
	beq	t0, t1, 7f  /* chr bank */
	lw	t2, OFFSET_PRGROM(WORK)
	bgez	t3, 8f
/* pbank
 * t1 が 0 なら 6000-7fff
 *       1      8000-9fff
 *       2      a000-bfff
 *       3      c000-dfff
 */
	mul	t3, t1, 0x2000
	addiu	t3, t3, 0x6000
	sll	t1, t1, 2
	addu	t1, t1, WORK
/* max check は 必要 (hebereke.nes) */
	lbu	t0, OFFSET_PBANK_MASK(WORK)
	and	t0, t0, a1
	mul	t4, t0, 0x2000
	subu	t3, t4, t3
	addu	t3, t3, t2
#if 0
/* debug */
	move	a1, t3
	subu	a2, t1, WORK
	PRINTF("Sun5P=%x,%x\n");
#endif
	sw	t3, OFFSET_PAGE_TABLE + 0x0c(t1)
	jr	STORE_DONE
8:
	andi	t0, a1, 0x02
	bgtz	t3, 9f
/* cmd 0x0c=mirror
 */
#if 0
/* debug */
	PRINTF("Sun5M=%x\n");
#endif
	andi	t1, a1, 0x01
	beq	t0, zero, 1f
/* d1: 1=one screen mode
 *     d0=0 なら 2000-?
 */
	addiu	t1, t1, 1
	andi	t1, t1, 0x02
	sll	t1, t1, 3 + 4 + 2
	sh	t1, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	sh	zero, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	jr	STORE_DONE
1:
	li	t0, 0x400
	beq	t1, zero, 1f
	li	t0, 0x800
1:
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	sh	zero, OFFSET_VRAM_MIRROR_MASK_OR(WORK)
	jr	STORE_DONE
7:
/* chr bank
 * t1 が 0..7 が 0x0000-,0x0400-,...,0x1c00- に対応
 */
	sll	t1, t1, 1
#if 0
/* debug */
	move	a2, t1
	PRINTF("Sun5C=%x,%x\n");
#endif
	addu	t1, t1, WORK
	sh	a1, OFFSET_CHRBANK_REG0000(t1)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

9:
/* t3 が 1 なら IRA enable/disable
 *       2      IRQ counter low
 *       3      IRQ counter high
 */
	addiu	t0, t3, -2
	bltz	t0, 1f
/* IRQ counter low or high
 * セット後 linecnt_irq に convert する。
 * フレームをまたぐと、IRQ がかからない ...
 */
	addu	t0, t0, WORK
	sb	a1, OFFSET_SUN5_IRQ_L(t0)
	lbu	t0, OFFSET_SUN5_IRQ_L(WORK)
	lbu	t1, OFFSET_SUN5_IRQ_H(WORK)
	lh	t3, OFFSET_LINECNT(WORK)
	sll	t1, t1, 8
	or	t0, t0, t1
	li	t2, (-(LINECYCLE_CONST0 + LINECYCLE_CONST1) / 0x100)
	divu	zero, t0, t2
	mflo	t0
	addu	t3, t3, t0
	sh	t3, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE

1:
/* 0x81 なら IRQ enable */
	xori	t0, a1, 0x81
	sltiu	t0, t0, 1
	sb	t0, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	jr	STORE_DONE

	GLOBAL(linecnt_over05)
linecnt_over05:
/* MMC5 ラインカウンタオーバー
 * 一致フラグをセット
 */
	lbu	t1, OFFSET_MMC5_EXRAM + 0x400 + 4(WORK)
	ori	t1, t1, 0x80
	sb	t1, OFFSET_MMC5_EXRAM + 0x400 + 4(WORK)
	
	li	t0, 0x8000
	sh	t0, OFFSET_LINECNT_IRQ(WORK)
	beq	t2, zero, mainloop_srlt0
	j	check_irq

	GLOBAL(linecnt_overqd)
linecnt_overqd:
/* disksys by myself
 * QD IRQ か、timer IRQ の、どちらでもやってくる。
 * QD 動作中なら、QD 割り込みにしてしまう！ええやろ。
 *
 * t4: new linecnt
 * t3: linecnt_irq preset 値
 * t2: linecnt_irq enabled
 *
 * QD
 * QD reset されてなくて motor ON で IRQ enable
 * なら QD 読み書き IRQ
 */
	lbu	t0, OFFSET_QD4020H + 5(WORK)
	andi	t1, t0, 0x80
	andi	$1, t0, 0xe3
	xori	$1, $1, 0xe1
	beq	t1, zero, 9f  /* QD IRQ ではない */
	bne	$1, zero, mainloop_srlt0  /* QD not active */
/* QD active !!
 * この line の終了時、すぐに linecnt over するように。
 * ただし linecnt > 238 なら -20 に戻す。
 */
	addiu	t3, t4, 1
	slti	t1, t3, 238
	bne	t1, zero, 1f
	li	t3, -20
1:
	lw	t1, OFFSET_QDIMAGE(WORK)
	lw	t2, OFFSET_QDOFFSET(WORK)
	addu	t1, t1, t2
	sh	t3, OFFSET_LINECNT_IRQ(WORK)

#if 0
/* debug */
	move a1,t3
PRINTF("OverQD=%x\n");
#endif
	
/* read? write? */
	andi	$1, t0, 0x04
	beq	$1, zero, check_irq  /* QD write - ここでは書込せず、割り込み起動のみ */
	lbu	t0, 0(t1)
	sb	t0, OFFSET_QD4030H + 1(WORK)
	j	check_irq

9:
/* check timer IRQ
 * 連続 ?
 */
	lbu	t0, OFFSET_QD4020H + 2(WORK)
	andi	t0, t0, 0x01
	li	t1, 0x8000
	beq	t0, zero, 1f  /* single */
	addu	t1, t3, t4
1:
	sh	t1, OFFSET_LINECNT_IRQ(WORK)
	sh	t1, OFFSET_VRC4_LINECNT_SAVE(WORK)
	li	t0, 0x01
	sb	t0, OFFSET_QD4030H(WORK)  /* timer overflow */
	beq	t2, zero, mainloop_srlt0
	j	check_irq

	GLOBAL(linecnt_over45)
linecnt_over45:
linecnt_over49:
linecnt_over12:
linecnt_over13:
linecnt_over5a:
linecnt_over21:
/* Sunsoft 5 ラインカウンタオーバー
 * リピートなし
 * t2: linecnt_irq enabled
 */
	li	t0, 0x8000
	sh	t0, OFFSET_LINECNT_IRQ(WORK)
	beq	t2, zero, mainloop_srlt0
	j	check_irq

	GLOBAL(linecnt_over19)
linecnt_over19:
/* VRC4 ラインカウンタオーバー
 * リピートなし
 * gradius2.nes 対策
 */
	addiu	LINECYCLE, LINECYCLE, -(6 * 0x100)
	li	t0, 0x8000
	sh	t0, OFFSET_LINECNT_IRQ(WORK)
	beq	t2, zero, mainloop_srlt0
	j	check_irq

	GLOBAL(linecnt_over43)
linecnt_over43:
/* Sunsoft 3 ラインカウンタオーバー
 * スクロールずれ fantasz2.nes 対策
 */
	addiu	LINECYCLE, LINECYCLE, 0x20 * 0x100
	li	t0, 0x8000
	sh	t0, OFFSET_LINECNT_IRQ(WORK)
	beq	t2, zero, mainloop_srlt0
	j	check_irq
		
	GLOBAL(linecnt_over18)
linecnt_over18:
/* VRC6
 * irq selector の d0=1 なら repeat
 * ブレ対策
 */
	addiu	LINECYCLE, LINECYCLE, 6 * 0x100
	lbu	t0, OFFSET_VRC6_IRQ_SELECT(WORK)
	bne	t2, zero, 1f
	li	t0, 0x8000
	sh	t0, OFFSET_LINECNT_IRQ(WORK)
	j	mainloop_srlt0
1:
	andi	t0, t0, 0x01
	bne	t0, zero, 1f
	li	t0, 0x8000
	sh	t0, OFFSET_LINECNT_IRQ(WORK)
	j	check_irq
1:
	lbu	t1, OFFSET_VRC6_IRQ(WORK)
	li	t3, 0x100
	subu	t1, t3, t1
	addu	t1, t1, t4
	sh	t1, OFFSET_LINECNT_IRQ(WORK)
#if 0
/* debug */
	move	a1, t1
	PRINTF("overNext=%x\n");
	xori	t0, a1, 0x0d
	beq	t0, zero, 1f
	sw	PC, OFFSET_TRACE_FLAG(WORK)
1:
#endif
	j	check_irq

	GLOBAL(store46_8000)
store46_8000:
/* Kitarou2 mapper
 * taito/arkanoi2.nes など。
 * 8000-ffff: Mpppcccc
 * M: mirror 0=縦 ?
 * ppp: chrbank @ 8000-bfff, 0x4000 bytes/bank
 * cccc: chrbank @ 0000-1fff, 0x2000 bytes/bank
 */
#if 0
/* debug */
	move	a2, a0
	lhu	a3, OFFSET_LINECNT(WORK)
	PRINTF("46(%x->%x)line=%x\n");
#endif
	lw	t1, OFFSET_PRGROM(WORK)
	lhu	t2, OFFSET_CHRBANK_REG0000(WORK)

/* d7=1 のとき 横持ち、d7=0 のとき one screen @ 2800- で いいの ?
 * taito/arkanoi2.nes は動く。kitarou2.nes もいけてそう。
 */
	li	t3, 0x400
	andi	t0, a1, 0x80
	xori	t0, t0, 0x80
	beq	t0, zero, 1f
	li	t3, 0
	li	t0, 0x800
1:
	sh	t3, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	sh	t0, OFFSET_VRAM_MIRROR_MASK_OR(WORK)

	andi	t0, a1, 0x70
	sll	t0, t0, 14 - 4
	addiu	t0, t0, -0x8000
	addu	t0, t0, t1
	sw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x14(WORK)

	andi	t3, a1, 0x0f
	sll	t3, t3, 3     /* 0x2000 bytes/bank */
	beq	t3, t2, store_done

	move	t0, WORK
	addiu	t1, t0, 0x10
1:
	sh	t3, OFFSET_CHRBANK_REG0000(t0)
	addiu	t0, t0, 2
	addiu	t3, t3, 1
	bne	t0, t1, 1b
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	
	GLOBAL(store49_a000)
store49_a000:
/* a000-afff: VRC3 IRQ L ?
 * b000-bfff:          H ?
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("VRC3_irq(%x->%x)\n");
#endif
	andi	t1, a0, 0x1000
	andi	t0, a1, 0x0f
	beq	t1, zero, 1f
	sll	t0, t0, 4
	li	t1, 1
1:
	addu	t1, t1, WORK
	sb	t0, OFFSET_VRC4_IRQ_L(t1)
	jr	STORE_DONE

	GLOBAL(store49_c000)
store49_c000:
/* VRC3 IRQ trigger ?
 * d1 = start?
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("VRC3_irq_trig(%x->%x)\n");
#endif
	lbu	t1, OFFSET_VRC4_IRQ_L(WORK)
	lbu	t2, OFFSET_VRC4_IRQ_H(WORK)
	lh	t3, OFFSET_LINECNT(WORK)
	
	andi	t0, a1, 0x02
	sb	t0, OFFSET_LINECNT_IRQ_ENABLED(WORK)

	or	t1, t1, t2
	li	t2, 0x17a
	subu	t1, t2, t1
	addu	t1, t1, t3
	sh	t1, OFFSET_LINECNT_IRQ(WORK)
	jr	STORE_DONE

	GLOBAL(store56_6000)
store56_6000:
/* Jaleco Moepro
 * debug
 * 6000-6fff: d5-d4: 0x8000 bytes 単位の pbank
 *            d7,d1,d0: 0x2000 bytes 単位の cbank
 * 7000-7fff: voice
 */
#if 0
/* debug */
	move	a3, PC
	move	a2, a0
	PRINTF("56(%x->%x),PC=%x\n");
#endif
	andi	t0, a0, 0x1000
	bne	t0, zero, 70f
/* まず pbank */
	lw	t2, OFFSET_PRGROM(WORK)
	lbu	t3, OFFSET_PBANK_MASK(WORK)

	sll	t3, t3, 2  /* 0x8000 bytes 単位 */
	and	t3, t3, a1
	andi	t3, t3, 0xf0
	addiu	t3, t3, -0x10  /* 後の -0x8000 に相当 */
	mul	t3, t3, 0x8000 / 0x10

	addu	t3, t3, t2

#if 0
/* debug */
/* debug */
	move	a2, t3
	PRINTF("%x,%x\n");
#endif
	sw	t3, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x1c(WORK)

/* chr bank - max check は無視 */
	lhu	t0, OFFSET_CHRBANK_REG0000(WORK)

	andi	t2, a1, 0x40
	srl	t2, t2, 4
	andi	t1, a1, 0x03
	or	t1, t1, t2
	
	sll	t1, t1, 3
	beq	t0, t1, store_done

#if 0
/* debug */
	lhu	a2, OFFSET_LINECNT(WORK)
	PRINTF("56_cbank=%x at line=%x\n");
#endif

/* 読み出し対策で 全 bank 設定 */
	move	t0, WORK
	addiu	t2, t0, 2 * 8
1:
	sh	t1, OFFSET_CHRBANK_REG0000(t0)
	addiu	t1, t1, 1
	addiu	t0, t0, 2
	bne	t0, t2, 1b
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	
	jr	STORE_DONE

70:
/* voice だ ...
 * moepro.nes の場合 (Sound_PlayMENUPCM の引数)
 * 10,30 - 初期化 ? 最初だけ
 * (1)20,30 - ストライク
 * (2)21,31 - ボール
 * (3)22,32 - ターイム
 * (4)23,33 - アウト
 * (5)24,34 - セーフ
 * (6)25,35 - ファール
 * (7)26,36 - フェア
 * (8)27,37 - バッターアウト(0x37 を連打している)
 * (9)28,38 - プレイボール
 * (a)29,39 - フォアボール
 * (b)2a,3a - ホームラン
 * (c)2b,3b - ピッチャー交代
 * (d)2c,3c - イテ (デッドボール)
 * (e)2d,3d - アーホー
 * (f)2e,3e - 打ったとき カーン
 * (0x10)2f,3f - 歓声 on/off
 * 2x を書き込んだときに Sound_PlayMENUPCM を呼ぶ。
 */
	andi	t0, a1, 0x10
	bne	t0, zero, store_done
#if 0
/* debug */
	move	a3, PC
	move	a2, a0
	PRINTF("56_voice(%x->%x),PC=%x\n");
#endif
	
	andi	a0, a1, 0x0f
	addiu	a0, a0, 1

	sw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	sw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	sw	gp, OFFSET_THREAD_GP(WORK)
#if 0
/* 0 もある。*/
	beq	a0, zero, store_done  /* 未対応 */
#endif
	jal	Sound_PlayMENUPCM
	
	lw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	lw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	lw	gp, OFFSET_THREAD_GP(WORK)
	
	jr	STORE_DONE

	GLOBAL(store58_8000)
store58_8000:
/* Namcot 118
 * MMC3 とほとんど同じだが R0 の 0x20 の位で mirror している。
 * (dbuster.nes)
 * map mode とかは なさそう だけど とりあえず MMC3 のコピー。
 */
#if 0
/* debug */
	move	a2, a0
	lhu	a3, OFFSET_LINECNT(WORK)
	PRINTF("58(%x->%x,%x)\n");
#endif
	lbu	$1, OFFSET_MMC3_POINTER(WORK)
	andi	t1, a0, 0x0001
	bne	t1, zero, 10f
/* MMC3 pointer - chr seg, map mode, chr bank reg 設定
 */
	sb	a1, OFFSET_MMC3_POINTER(WORK)
/* chrrom 読み出し用 chr seg 設定 */
	andi	t2, a1, 0x80
	sll	t2, t2, 5  /* 0x80 -> 0x1000 */
	sh	t2, OFFSET_CHRSEG_FOR_READ(WORK)
	xor	t0, $1, a1

	andi	t1, t0, 0x80
	beq	t1, zero, 1f
/* chr seg が変わったので BG, OBJ bank が変わったと考える。*/
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
1:
	andi	t1, t0, 0x40
	beq	t1, zero, store_done
/* map mode が変わった。
 */
	lbu	t1, OFFSET_MMC3_R6(WORK)
	lw	t3, OFFSET_PRGROM(WORK)
	lw	t4, OFFSET_PRGSIZE(WORK)
	andi	t2, a1, 0x40
	bne	t2, zero, 41f
/* map mode 0 は window 0 (R6) が 8000-9fff. c000-dfff は prgrom + prgsize - 0x4000 固定。*/
	addiu	t1, t1, -4  /* 後で -0x8000 相当になる */
	mul	t1, t1, 0x2000
	addu	t1, t1, t3
	sw	t1, OFFSET_PAGE_TABLE + 0x10(WORK)
	addu	t3, t3, t4
	li	t4, -0x4000 - 0xc000
	addu	t3, t3, t4
	sw	t3, OFFSET_PAGE_TABLE + 0x18(WORK)
	jr	STORE_DONE
41:
/* map mode 1 は window 0 (R6) が c000-dfff. 8000-9fff は prgrom + prgsize - 0x4000 固定。*/
	addiu	t1, t1, -6  /* 後で -0xc000 相当になる */
	mul	t1, t1, 0x2000
	addu	t1, t1, t3
	sw	t1, OFFSET_PAGE_TABLE + 0x18(WORK)
	addu	t3, t3, t4
	li	t4, -0x4000 - 0x8000
	addu	t3, t3, t4
	sw	t3, OFFSET_PAGE_TABLE + 0x10(WORK)
	jr	STORE_DONE
	
10:
	
/* レジスタ設定 Namcot 118 ですよ。
 * $1: pointer
 * t0: pointer & 7
 * t1: t0 + WORK
 * t2: 前回書いた値
 */
	andi	t0, $1, 0x07
	
	addu	t1, t0, WORK
	lbu	t2, OFFSET_MMC3_R0(t1)
	beq	t2, a1, store_done     /* 前回と同じ値 */
/* R0,R1 なら d0 を無視して chr bank for 0000-, 0800- を。
 * R2-R5 なら chr bank for 1000-,1400-,1800-,1c00- を。
 * R6,R7 なら prg bank.
 */
	lbu	t2, OFFSET_PBANK_MASK(WORK)
	addiu	t3, t0, -6
	bgez	t3, 6f
	lw	t2, OFFSET_CHR_OFFSET_MASK(WORK)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	mul	t4, a1, 0x400
#if 0
/* debug */
	ori	a2, t4, 0x3ff
	and	a3, a2, t2
	tne	a2, a3
#endif
	and	t4, t4, t2
	srl	t4, t4, 10  /* / 0x400 */
/* d5 で mirror
 */
	andi	t3, a1, 0x20
	sll	t3, t3, 1 + 4  /* 0x20 -> 0x400 */
	sh	t3, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
	
	addiu	t3, t0, -2
	bgez	t3, 2f
/* R0,R1 - d0 は無効 */
	andi	t4, t4, 0xfffe
	sb	t4, OFFSET_MMC3_R0(t1)
	sll	t0, t0, 2
	addu	t0, t0, WORK
	sh	t4, OFFSET_CHRBANK_REG0000(t0) /* 0000 or 0800 */
	addiu	t4, t4, 1
	sh	t4, OFFSET_CHRBANK_REG0400(t0) /* 0400 or 0c00 */
	jr	STORE_DONE
2:
/* R2-R5
 * chr bank for 1000-, 1400-, 1800-, 1c00- を。
 * t3: t0 - 2
 */
	sb	t4, OFFSET_MMC3_R0(t1)
	sll	t3, t3, 1
	addu	t3, t3, WORK
	sh	t4, OFFSET_CHRBANK_REG1000(t3)
	jr	STORE_DONE
6:
/* R6-R7 pbank.
 * R6 なら mapmode によって window が違う。
 * $1: pointer
 * t0: pointer & 7
 * t1: t0 + WORK
 * t2: pbank mask
 * t3: t0 - 6
 */
	lw	t0, OFFSET_PRGROM(WORK)
	and	t2, t2, a1
	sb	t2, OFFSET_MMC3_R0(t1)
	bne	t3, zero, 7f
/* R6 */
	andi	$1, $1, 0x40
	beq	$1, zero, 40f
/* map mode 1 - R6 は c000-dfff */
	addiu	t2, t2, -6  /* 後で -0xc000 相当になる */
	mul	t2, t2, 0x2000
	addu	t2, t2, t0
	sw	t2, OFFSET_PAGE_TABLE + 0x18(WORK)
	jr	STORE_DONE
40:
/* map mode 0 - R6 は 8000-9fff */
	addiu	t2, t2, -4  /* 後で -0x8000 相当になる */
	mul	t2, t2, 0x2000
	addu	t2, t2, t0
	sw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	jr	STORE_DONE
7:
/* R7 - mapmode にかかわらず a000-bfff */
	addiu	t2, t2, -5  /* 後で -0xa000 相当になる */
	mul	t2, t2, 0x2000
	addu	t2, t2, t0
	sw	t2, OFFSET_PAGE_TABLE + 0x14(WORK)
	jr	STORE_DONE
	
	GLOBAL(store5a_8000)
store5a_8000:
/* PC-JY-? mapper for o/cracked/tekken2.nes, o/cracked/samuspi2.nes
 * 8000: pbank for 8000-9fff ?
 * 8001: pbank for a000-bfff ?
 * 8002: pbank for c000-dfff ?
 * 8003: pbank for e000-ffff ?
 * 0x2000 bytes/bank でしょう。
 * 9000-9007: chrbank low for 0000-,0400-,...,1c00- ?
 */
	andi	t0, a0, 0x0003
	andi	t3, a0, 0x1000
	bne	t3, zero, 10f
	sll	t1, t0, 2
	addu	t1, t1, WORK
	lw	t2, OFFSET_PRGROM(WORK)
	lbu	t3, OFFSET_PBANK_MASK(WORK)
	and	t3, t3, a1
	addiu	t0, t0, 4  /* 後の -0x8000 に相当 */
	subu	t3, t3, t0
	mul	t3, t3, 0x2000
	addu	t3, t3, t2
	sw	t3, OFFSET_PAGE_TABLE + 0x10(t1)
	jr	STORE_DONE
10:
#if 0
/* debug */
	move	a2, a0
	PRINTF("5a(%x->%x)\n");
#endif
	andi	t0, a0, 0x0007
	sll	t0, t0, 1
	addu	t0, t0, WORK
	sb	a1, OFFSET_CHRBANK_REG0000 + 1(t0)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	GLOBAL(store5a_a000)
store5a_a000:
/* 
 * a000-a007: chrbank high ?
 */
	andi	t0, a0, 0x0007
	andi	t3, a0, 0x1000
	bne	t3, zero, 10f
	sll	t0, t0, 1
	addu	t0, t0, WORK
	sb	a1, OFFSET_CHRBANK_REG0000 + 0(t0)
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE
10:
#if 0
/* debug */
	move	a2, a0
	PRINTF("5a(%x->%x)\n");
#endif
	jr	STORE_DONE

	GLOBAL(store5a_c000)
store5a_c000:
/* c002/c005, c004/c003 に同じ値 ?
 * c005: irq lines, c003: enable にしてみよう。
 */
#if 0
/* debug */
	move	a2, a0
	lhu	a3, OFFSET_LINECNT(WORK)
	PRINTF("5a_irq(%x->%x)line=%x\n");
#endif
	xori	t0, a0, 0xc005
	xori	t1, a0, 0xc003
	beq	t0, zero, 5f
	xori	t0, a0, 0xc002
	beq	t1, zero, 3f
	beq	t0, zero, 5f

	jr	STORE_DONE
5:
	lh	t2, OFFSET_LINECNT(WORK)
	move	t3, a1
	bltz	t2, 1f
	addu	t3, t2, a1
1:
	sh	t3, OFFSET_LINECNT_IRQ(WORK)
	sb	a1, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	jr	STORE_DONE

3:
	sb	a1, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	jr	STORE_DONE
	
	GLOBAL(store5c_8000)
store5c_8000:
/* Jaleco Moepro88
 *
 * 書き込む rom address には、書き込む値と同じものが格納されている
 * 場合がほとんど。
 * プロテクトかも。
 * 80b0-80bf: pbank @ c000-ffff, 0x4000 bytes/bank
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("5c(%x->%x)\n");
#endif

	.set	noreorder
	andi	t0, a0, 0x00f0
	xori	t1, t0, 0xb0
	beq	t1, zero, 11f
	nop
	.set	reorder

	
11:
/* pbank @ c000-ffff */
	
		
	jr	STORE_DONE

		
	
		
	GLOBAL(store63_4020)
store63_4020:
#if 0
/* debug */
	PRINTF("63_4020=%x\n");
#endif
	jr	STORE_DONE

	GLOBAL(storee6_8000)
storee6_8000:
/* 22 in 1
 * 手抜きだけど、これで どう ?
 */
	andi	a0, a0, 0xfffe
	andi	a1, a1, 0x7f
/* thru */

	GLOBAL(storee2_8000)
storee2_8000:
/* 76 in 1
 * 8000,8002,..., fffe: | p4 | M | Z | p3-p0 | q |
 * 8001,8003,..., ffff:     - - - - - - | w | p5 |
 * M: mirror 0 で縦持ち ?
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("e2(%x->%x)\n");
#endif
	andi	t0, a0, 0x0001
	addu	t1, t0, WORK
	sb	a1, OFFSET_SS_PBANK0(t1)
	bne	t0, zero, 10f
/* 8000,8002,...
 * M のみ処理
 */
	andi	t0, a1, 0x40
	sll	t0, t0, 4     /* 0x40 -> 0x400 */
	li	t1, 0x800
	subu	t0, t1, t0
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)
10:
	lhu	t0, OFFSET_SS_PBANK0(WORK)
/* chrram protect ? */
	la	t2, store_2007_chrram
	andi	t1, t0, 0x0002
	beq	t1, zero, 2f
	la	t2, store_2007_chrrom
2:
	sw	t2, OFFSET_STORE_PPU_FUNCTBL + 7 * 4(WORK)

	andi	t1, t0, 0x8000
	andi	t2, t0, 0x1e00
	andi	t3, t0, 0x0001
	sll	t3, t3, 5        /* p5 */
	srl	t2, t2, 1 + 8    /* p3-p0 */
	srl	t1, t1, 3 + 8    /* p4 */
	or	t1, t1, t2
	or	t1, t1, t3
	sll	t1, t1, 15  /* * 0x8000 */
	addiu	t1, t1, -0x8000
	lw	t2, OFFSET_PRGROM(WORK)
	addu	t3, t2, t1
	move	t4, t3
/* t0: register data
 * t1: bank no
 * t2: prgrom
 * t3: page table 8000-,a000-
 * t4:            c000-,e000-
 */
/* 0x8000 bytes/bank? 0x4000 bytes/bank ? */
	andi	$1, t0, 0x2000
	beq	$1, zero, 1f
/* 0x4000 bytes/bank
 * 8000-bfff, c000-ffff のどっちが有効 ?
 */
	andi	$1, t0, 0x0100
	addiu	t3, t3, 0x4000
	bne	$1, zero, 1f
/* 8000-bfff が有効 */
	addiu	t3, t3, -0x4000
	addiu	t4, t4, -0x4000
1:
	sw	t3, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t4, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t4, OFFSET_PAGE_TABLE + 0x1c(WORK)
	jr	STORE_DONE
	
	GLOBAL(storee3_8000)
storee3_8000:
/* 1200in1
 * a9|            |a0
 * HL|p|f|pppp|h|m|s
 * 書き込む値ではなく、そのアドレスに意味がある。
 *
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("e3(%x->%x)\n");
#endif

/* a1: mirror 0=横持ち */
	li	t0, 0x400
	andi	t2, a0, 0x0002
	beq	t2, zero, 1f
	li	t0, 0x800
1:
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)

/* a8,a6-a3: 0x8000 bytes 単位の select
 * この top を t4 に。offset を t3 に。
 */
	lw	t4, OFFSET_PRGROM(WORK)
	andi	t0, a0, 0x0100
	andi	t1, a0, 0x0078
	srl	t0, t0, 1
	or	t0, t0, t1
	sll	t3, t0, 12  /* 0x08 -> 0x8000 */
	addu	t1, t4, t3

/* a0: page size
 * 1 なら 32KB prg rom
 * 0 なら 16KB prg rom. a2 が 1 なら top += 0x4000
 */
	li	t0, -0x8000
	addu	t2, t0, t1
	sw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x1c(WORK)

	andi	t0, a0, 0x01
	andi	t1, a0, 0x04
	bne	t0, zero, 4f
	bne	t1, zero, 1f
/* 8000-bfff を c000-ffff にも。*/
	addiu	t2, t2, -0x4000
	sw	t2, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x1c(WORK)
	j	4f
1:
/* c000-ffff を 8000-bfff にも。*/
	addiu	t2, t2, 0x4000
	sw	t2, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x14(WORK)
4:
/* page fixing ?
 */
	la	t0, store_2007_chrrom
	sw	t0, OFFSET_STORE_PPU_FUNCTBL + 7 * 4(WORK)
	
	andi	t0, a0, 0x0080
	andi	t1, a0, 0x0200
	bne	t0, zero, 8f
/* page fix のときは chrram write enable */
	la	t0, store_2007_chrram
	sw	t0, OFFSET_STORE_PPU_FUNCTBL + 7 * 4(WORK)
	
	li	t2, 0x1c000
	or	t3, t3, t2
	bne	t1, zero, 2f
	xor	t3, t3, t2
2:
	addu	t3, t3, t4
	li	t2, -0xc000
	addu	t2, t2, t3
	sw	t2, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t2, OFFSET_PAGE_TABLE + 0x1c(WORK)

8:
	jr	STORE_DONE

	GLOBAL(storee0_8000)
storee0_8000:
/* 72 in 1
 * A14 を 0 にして 110 in 1 と同様に。
 */
	andi	a0, a0, 0xbfff
/* thru */
	GLOBAL(storee1_8000)
storee1_8000:
/* 110 in 1
 * 書き込む address に意味がある。
 * a14: prg rom, chr rom の lower half, upper half の selector.
 *      うーん、とりあえず 110 in 1 only として
 *      1 なら prgrom offset + 0x100000, chrrom offset +0x80000 に。
 * a13: 横持ち or 縦持ち
 * a12: 0=0x8000 bytes/bank, 1=0x4000 bytes/bank
 *  -> 0x4000 bytes/bank のとき a6 によって 前半, 後半 どっちを mirror するか。
 * a11-a7: pbank 0x8000 bytes 単位
 * a5-a0: chrbank 0x2000 bytes 単位
 */
#if 0
/* debug */
	move	a1, a0
	move	a2, PC
	PRINTF("e1(%x)PC=%x\n");
#endif

	andi	t0, a0, 0x2000
	srl	t0, t0, 4 - 1   /* 0x2000 -> 0x400 */
	addiu	t0, t0, 0x400
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)

/* t0: temp
 * t1: prgrom -> temp
 * t2: 0 or 0x100000, select lower/upper rom
 * t3: page table 8000-,a000-
 * t4:            c000-,e000-
 */
	andi	t2, a0, 0x4000
	sll	t2, t2, 2 + 4   /* 0 or 0x100000 */
	lw	t1, OFFSET_PRGROM(WORK)
	andi	t0, a0, 0x0f80
	sll	t0, t0, 15 - 7  /* 0x80 -> 0x8000 */
	addu	t0, t0, t2
#if 0
/* debug */
	move	a1, t0
	PRINTF("e1_prg_offset=%x\n");
#endif
	
	addiu	t0, t0, -0x8000
	addu	t3, t0, t1
	move	t4, t3
	
/* 0x8000 bytes/bank? 0x4000 bytes/bank ? */
	andi	t1, a0, 0x1000
	beq	t1, zero, 1f
/* 0x4000 bytes/bank
 * 8000-bfff, c000-ffff のどっちが有効 ?
 */
	andi	t0, a0, 0x0040
	addiu	t3, t3, 0x4000
	bne	t0, zero, 1f
/* 8000-bfff が有効 */
	addiu	t3, t3, -0x4000
	addiu	t4, t4, -0x4000
1:
	sw	t3, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t3, OFFSET_PAGE_TABLE + 0x14(WORK)
	sw	t4, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t4, OFFSET_PAGE_TABLE + 0x1c(WORK)

/* chrbank
 * t2: 0 or 0x100000, lower/upper -> 0 or 0x200 に。
 */
	srl	t2, t2, 8 + 3
	andi	t0, a0, 0x3f
	sll	t0, t0, 3    /* 0x400 bytes/bank -> 0x2000 bytes/bank */
	or	t0, t0, t2

	move	t1, WORK
	addiu	t2, t1, 0x10
1:
	sh	t0, OFFSET_CHRBANK_REG0000(t1)
	addiu	t0, t0, 1
	addiu	t1, t1, 2
	bne	t1, t2, 1b
	
	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)
	jr	STORE_DONE

	GLOBAL(storee5_8000)
storee5_8000:
/* 31 in 1
 * 書き込むアドレスに意味がある。
 * 8000-ffff の、A5: mirror
 * A4-A0: chrbank/pbank
 * ただし、pbank entry #0 は smario.nes で、#0,#1 は #0 と同じ扱い。
 */
#if 0
/* debug */
	move	a2, a0
	PRINTF("e5(%x->%x)\n");
#endif

/* mirror */
	andi	t0, a0, 0x20
	sll	t0, t0, 1 + 4  /* 0x20 -> 0x400 */
	addiu	t0, t0, 0x400
	sh	t0, OFFSET_VRAM_MIRROR_MASK_AND(WORK)

/* chrbank */
	andi	t0, a0, 0x1f
	sll	t0, t0, 3
	move	t1, WORK
	addiu	t2, t1, 0x10
1:
	sh	t0, OFFSET_CHRBANK_REG0000(t1)
	addiu	t0, t0, 1
	addiu	t1, t1, 2
	bne	t1, t2, 1b

	sb	zero, OFFSET_BG_CHRBANK_TOUCH_FLAG(WORK)
	sb	zero, OFFSET_OBJ_CHRBANK_TOUCH_FLAG(WORK)

/* pbank */
	andi	t0, a0, 0x1e
	li	t2, 0x4000
	bne	t0, zero, 1f  /* 16KB mode */
	li	t2, 0
	andi	a0, a0, 0xfffe
1:
	lw	t1, OFFSET_PRGROM(WORK)
	andi	t0, a0, 0x1f
	mul	t0, t0, 0x4000
	addiu	t0, t0, -0x8000
	addu	t0, t0, t1
	sw	t0, OFFSET_PAGE_TABLE + 0x10(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x14(WORK)
	subu	t0, t0, t2
	sw	t0, OFFSET_PAGE_TABLE + 0x18(WORK)
	sw	t0, OFFSET_PAGE_TABLE + 0x1c(WORK)
#if 0
/* debug */
	lw	a1, OFFSET_CHRBANK_REG0000(WORK)
	lw	a2, OFFSET_CHRBANK_REG1000(WORK)
	PRINTF("%08x %08x\n");
#endif
	jr	STORE_DONE

	GLOBAL(storeqd_4020)
storeqd_4020:
	GLOBAL(storeqd_4021)
storeqd_4021:
/* timer l,h -> 逆に。
 * endianness 依存 !!
 */
	xori	t0, a0, 0x01
	addu	t0, t0, WORK
	sb	a1, OFFSET_QD4020H - 0x4020(t0)
	jr	STORE_DONE
	
	GLOBAL(storeqd_4022)
storeqd_4022:
/* timer IRQ -> line 単位で扱う。
 * d1: IRQ enable
 * d0: 0 = 1 回、1 = 連続 (でも、フレームをまたぐと、うまくかからない)
 * see also: store45_a000
 */
	lhu	t0, OFFSET_QD4020H(WORK)
	sb	a1, OFFSET_QD4020H + 2(WORK)
	andi	t1, a1, 0x02
	sb	t1, OFFSET_LINECNT_IRQ_ENABLED(WORK)
	lh	t3, OFFSET_LINECNT(WORK)

	li	t2, (-(LINECYCLE_CONST0 + LINECYCLE_CONST1) / 0x100)
	divu	zero, t0, t2
	mflo	t0

	addu	t3, t3, t0
	sh	t3, OFFSET_LINECNT_IRQ(WORK)
/* linecnt_irq は QD IRQ を使うと破壊される */
	sh	t3, OFFSET_VRC4_LINECNT_SAVE(WORK)
/* 一応 連続 対策 */
	sh	t0, OFFSET_LINECNT_IRQ_PRESET(WORK)
	jr	STORE_DONE

	GLOBAL(storeqd_4023)
storeqd_4023:
/* 4023H は Sound_Write も行う */
	sb	a1, OFFSET_QD4020H + 3(WORK)
	j	store_sound_4023

	GLOBAL(storeqd_4025)
storeqd_4025:
/* 4025: d7=1: IRQ enable
 *       d6: 0= header 1=data
 *       d5: 1
 *       d4: 1=check CRC --> 2 bytes skip
 *       d3: 0 = 横持ち 1 = 縦持ち
 *       d2: /WRGATE 0 で WR
 *       d1: /MOTORON
 *       d0: /QDRES
 */
	sb	a1, OFFSET_QD4020H + 5(WORK)
/* VRAM mirror */
	andi	$1, a1, 0x08
	sll	$1, $1, 7    /* 0x08 -> 0x400 */
	addiu	$1, $1, 0x400
	sh	$1, OFFSET_VRAM_MIRROR_MASK_AND(WORK)

/* IRQ on ?
 *
 * on なら line 終了時 すぐに IRQ がかかるように。
 * (ただし linecnt > 238 なら -20 にする)
 * off なら linecnt_save から戻しておく
 */
	andi	t0, a1, 0x80
	lh	t1, OFFSET_VRC4_LINECNT_SAVE(WORK)
	beq	t0, zero, 1f
	lh	t1, OFFSET_LINECNT(WORK)
	addiu	t1, t1, 1
	slti	t0, t1, 238
	bne	t0, zero, 1f
	li	t1, -20
1:
	sh	t1, OFFSET_LINECNT_IRQ(WORK)
	andi	t0, a1, 0x03
#if 0
/* debug */
	move a1,t1
	PRINTF("4025 IRQ=%x\n");
#endif
	
/* QD reset ? */
	lhu	t2, OFFSET_QDOFFSET + 2(WORK)
	xori	t0, t0, 0x02
	bne	t0, zero, store_done
	lbu	t1, OFFSET_QD4030H + 2(WORK)
	sh	zero, OFFSET_QDOFFSET + 2(WORK)
	andi	t1, t1, 0xfd
	sb	t1, OFFSET_QD4030H + 2(WORK)
	jr	STORE_DONE

	GLOBAL(storeqd_4026)
storeqd_4026:
	sb	a1, OFFSET_QD4030H + 3(WORK)
	jr	STORE_DONE

	GLOBAL(storeqd_8000)
storeqd_8000:
/* emu-cpu address が 0x8000-0xdfff のときのみ来る。
 * prgrom + 0 .. prgrom + 0x5fff に書き込み。
 */
#if 0
/* debug */
	move a2,a0
	PRINTF("qd(%x->%x)\n");
#endif
	lw	t1, OFFSET_PRGROM(WORK)
	addu	t0, a0, t1
	sb	a1, -0x8000(t0)
	jr	STORE_DONE
	
	GLOBAL(storeqd_4024)
storeqd_4024:
	lbu	t0, OFFSET_QD4020H + 5(WORK)
	lw	t1, OFFSET_QDIMAGE(WORK)
	lw	t2, OFFSET_QDOFFSET(WORK)
	andi	t0, t0, 0x87
	xori	t0, t0, 0x81
	bne	t0, zero, store_done
	addu	t1, t1, t2
	sb	a1, -2(t1)
	sb	zero, OFFSET_QD_DIRTY(WORK)
	jr	STORE_DONE
	
	GLOBAL(wdm_42)
wdm_42:
/* disksys の システム ROM 空間 で実行されたなら、
 * 特別な意味を持つ。
 * (特別な場所に 埋め込んである)
 * それ以外は undef_op
 */
	lw	t0, OFFSET_QDIMAGE(WORK)
	li	t1, 0xe000
	sltu	t1, PC, t1
	beq	t0, zero, undef_op
	bne	t1, zero, undef_op  /* disksys だけど PC < 0xe000 */
/* 実際には、「特別な場所」とは
 * diskedit.bin
 * 0xe406: 85 01  sta 01  ->  42 01  wdm 01
 * の場所である。
 * ここでは、QD インフォメーションブロックへのポインタが確定したとき
 * に来る。
 * 本来 sta 01 なのでまず、その処理を行う。
 */
	lbu	t0, OFFSET_WRAM + 0(WORK)
	sb	A, OFFSET_WRAM + 1(WORK)

/* emu-cpu address (0x00,0x01) を L,H のポインタとして
 * インフォメーションブロックのデータを頂く。
 * I/O port じゃなく、どうせ RAM or ROM 上にしかないんだから
 * page table 経由で参照。
 */
	sll	t1, A, 8
	or	t0, t0, t1
/* maker code は 自明なので 頂かない。*/
	addiu	t0, t0, 1
/* t0 = emu-cpu address
 * t1 = loop address
 * t2 = loop address end
 */
	move	t1, WORK
	addiu	t2, t1, 8
1:
/* 
 * page table 参照のため、0x2000 -> 0x04 (d1,d0 は 00 に)
 */
	srl	t3, t0, 13
	sll	t3, t3, 2
	addu	PAGE_PTR, t3, WORK
	lw	t3, OFFSET_PAGE_TABLE(PAGE_PTR)
	addu	t3, t3, t0
	lbu	t3, 0(t3)
	sb	t3, OFFSET_QD_INFO_BLOCK(t1)
	addiu	t0, t0, 1
	addiu	t1, t1, 1
	bne	t1, t2, 1b

	lw	t0, OFFSET_QD_INFO_BLOCK(WORK)
	lw	t1, OFFSET_QD_INFO_BLOCK + 4(WORK)

/* t0 が 0xffffffff なら、disk, side ともに変更しない。*/
	addiu	t2, t0, 1
	beq	t2, zero, next

/* じゃあ、qdimage + 0x10 のところに game code, ver, side, vol があるので
 * 4 回まで compare していこう。
 * t0,t1 = compare source data
 * $1 = temp
 * t2 = qdimage
 * t3 = loop qdimage
 * t4 = loop qdimage end
 */
	lw	t2, OFFSET_QDIMAGE(WORK)
	lw	t3, OFFSET_QDIMAGE(WORK)
	li	t4, 0x40000
	addu	t4, t4, t2
1:
	lw	$1, 0x10(t3)
	bne	$1, t0, 2f
	lw	$1, 0x14(t3)
	beq	$1, t1, 3f
2:
	li	$1, 0x10000
	addu	t3, t3, $1
	bne	t3, t4, 1b
/* あれ!? ない ... */
	move	t3, t2
3:
/* 発見！この qd offset で。*/
	subu	t3, t3, t2
	sw	t3, OFFSET_QDOFFSET(WORK)
	j	next

undef_op:
/* debug */
	TRAP("UNDEFINED OP(v0)");
1:
	j	1b

	GLOBAL(linecnt_over00)
linecnt_over00: /* 起こってはいけないのに 起こってしまったら無限ループ。*/
	TRAP("UNEXPECTED LINECNT OVER");
	j	linecnt_over00
	
	
		
	GLOBAL(convert_chrdata)
convert_chrdata:
/* a0: chrrom top (keep!)
 * a1: chrrom offset (keep!)
 * a2: dst top (keep!)
 * a3: keep!
 * chr data コンバートのアルゴリズムは
 * offset は 0x10 bytes 単位。
 * (offset & 0x000e) が chrline で 8 通りあるが、これによって
 * 結果の offset が +0,+0x80,+0x100,...,+0x380 となる。
 * 0x40 chars/bank だから。
 * (offset & 0x03f0) が chr.no で 0x40 通りあり、これは
 * 結果の offset +0,+2,+4,...,+0x7e となる。
 * それより上位 bit は そのまま使う。
 *
 */
	andi	t0, a1, 0x0e
	sll	t0, t0, 7    /* 0x0e -> 0x380 */
	andi	t1, a1, 0x3f0
	srl	t1, t1, 3    /* 0x3f0 -> 0x7e */
	or	t0, t0, t1
	li	t1, 0xfffc00
	and	t1, t1, a1
	or	t0, t0, t1
	addu	t1, t0, a2
	addu	t0, a0, a1
/*
 * t0: src addr top
 * t1: dst addr top
 */
	addiu	t4, t0, 8
1:
	lbu	t2, 0(t0)
	lbu	t3, 8(t0)
	sll	t3, t3, 8
	or	t3, t3, t2
	sh	t3, 0(t1)
	addiu	t0, t0, 1
	addiu	t1, t1, 0x80
	bne	t0, t4, 1b
	jr	ra

	GLOBAL(emuframe_done)
emuframe_done:
#if 0
/* debug */
	PRINTF("emuframe_done\n");
#endif

	
/* a2 が再開するべき PC
 * store 関数などからの suspend で ra を破壊しないように。
 */
	sw	ra, OFFSET_THREAD_RA(WORK)
	sw	a2, OFFSET_THREAD_PC(WORK)
	sw	STORE_DONE, OFFSET_THREAD_STORE_DONE(WORK)
	sw	JUMPTBLP, OFFSET_THREAD_JUMPTBLP(WORK)
	sw	PAGE_PTR, OFFSET_THREAD_PAGE_PTR(WORK)
	sw	LINECYCLE, OFFSET_THREAD_LINECYCLE(WORK)
	
	sh	PC, OFFSET_PC(WORK)
	sb	A, OFFSET_A(WORK)
	sb	X, OFFSET_X(WORK)
	sb	Y, OFFSET_Y(WORK)
	sb	SP, OFFSET_SP(WORK)
	sb	Z, OFFSET_WORK_Z(WORK)
	sb	C, OFFSET_WORK_C(WORK)
	sb	V, OFFSET_WORK_V(WORK)
	sb	N, OFFSET_WORK_N(WORK)

	lw	s0, OFFSET_STACK_S0(sp)
	lw	s1, OFFSET_STACK_S1(sp)
	lw	s2, OFFSET_STACK_S2(sp)
	lw	s3, OFFSET_STACK_S3(sp)
	lw	s4, OFFSET_STACK_S4(sp)
	lw	s5, OFFSET_STACK_S5(sp)
	lw	s6, OFFSET_STACK_S6(sp)
	lw	s7, OFFSET_STACK_S7(sp)
	lw	gp, OFFSET_STACK_GP(sp)
	lw	fp, OFFSET_STACK_FP(sp)
	lw	ra, OFFSET_STACK_RA(sp)
	
	addiu	sp, sp, EMU_RFC_ASM_STACKSIZE
	jr	ra

	.end	ks_emu_rfc_asm
	
/* extern void ks_clear_r4300_watch(void); */
	.globl	ks_clear_r4300_watch
	.ent	ks_clear_r4300_watch	
ks_clear_r4300_watch:
	.set	noreorder
	mtc0	zero, C0_WATCHLO
	jr	ra
	nop
	.set	reorder
	.end	ks_clear_r4300_watch
