| 
 | 
 
 本帖最后由 yandagui 于 2017-4-29 10:04 编辑  
* ?9 {9 y  U+ N1 v1 H. ? 
- x' y1 _7 V" D& Z. h1 B4 \[FC][SRAM扩容教程(Mapper 4为例)] 
$ E$ ^1 |" y% U! f9 C 
( Z( U8 a; E4 f时间:2017.4.28 
) j: z# T4 {' h5 U* Z$ D作者:FlameCyclone 
) [0 d  _# }# i$ Y& N工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator: H& W- |" y5 h4 {  n  |4 }1 M  x3 a 
ROM:双截龙2(J).nes2 X  Y& r' t2 p2 S$ R# g1 G0 ^- X 
适用:没有使用SRAM的ROM0 T% G3 c2 [" T, K# O 
$ @3 W1 ?1 m. ^& @) i3 r 
首先用Hxd打开ROM:% }; T" V. i3 G1 V  G# k 
 / e4 ]2 m. H& Q/ Z 
然后扩容:" G9 Y1 U: n& K8 z+ i* R 
 
3 n  e$ S6 T$ _& d+ W* p! A3 N0 E/ a+ b) r 
 
& k' o3 P$ C+ z 
/ |* z6 t/ i" H$ k0 H! Z1 l9 O. t  C; K1 v3 P% w  f( u# V+ z 
 
" B+ w! e# i3 J" x6 q) n4 e# C2 i, W- i, }4 J- t# b 
 
5 }8 Q; E+ K& i2 z5 T# H) u8 M6 Z 
; r# z  v0 I% Y" W9 d; @9 Q6 }1 C, b5 _先看看任天堂产品系统文件对NES文件的说明: 
) O# q. d" J3 Z1 c3 zNES文件格式 
. U7 Y2 b6 Z6 @0 J. K: p.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。  
0 A1 I  }" w4 b, K/ w$ ]6 l偏移         字节数         内容  
. ]) P+ B: a1 @( e3 M- V; N& ~0-3         4         字符串“NES^Z”用来识别.NES文件  
: i2 V* J5 w+ G. Q1 n) P4 s4         1         16kB ROM的数目 ( c" y( q8 }: y, Z, K! R 
5         1         8kB VROM的数目 + S4 X  l& e) B6 p$ @ 
6         1         D0:1=垂直镜像,0=水平镜像 ! I! Z, q2 g# o 
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF ( S6 i: d7 M+ A) k( i* P2 G* E4 k 
                    D2:1=在$7000-$71FF有一个512字节的trainer " U. `& q0 @: }, k  k. s3 ` 
                    D3:1=4屏幕VRAM布局  
: X& ?% @% `. O; P                    D4-D7:ROM Mapper的低4位 ' o0 O0 a$ V, h5 d2 o3 Y3 [# u" y. u 
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)  
$ V' K5 i- c* U6 T                    D4-D7:ROM Mapper的高4位  
7 z$ b  l) Q0 ~2 _8-F         8         保留,必须是0 ) i, ^8 K6 S* s4 R  W 
16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前  
( l6 V/ r$ {! U) F, U7 R9 p: A9 P+ K-EOF         8KxN         VROM段, 升序排列 " ?2 `4 Q" Q3 K4 t 
6 V" R% r. f/ B/ s' N& \6 i3 y 
然后知道这个ROM有0x08个PROM和0x10个VROM 
# F* j4 |7 t9 M; x接下来扩展PROM位0x10个:$ G  G' K8 i3 C, F9 p 
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM): 
6 v* f( K3 Q# S3 y. \  
9 ]1 h  N9 b! a由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:- G5 M; a" ~2 f8 v: d; a  v) S3 w 
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。6 c  p" t5 A2 R1 s) j 
于是可以得到双截龙2的是: 
( \7 H9 J" `- k(8-1)x 0x4000 + 0x10 = 1C010。4 \& U2 u/ _; ~5 ?, { 
然后跳转到1C010: 
1 u5 ~5 n& t- ~% O  
: g- D1 j, E" ?$ ^; L9 N( V5 U 3 P! g$ w: ^7 u8 e/ b 
  
$ m; R, c1 H0 e+ V然后插入0x20000字节的FF:9 Y5 P3 o6 W8 w 
  
: s: E+ ?7 S$ _8 j3 d  
* Q2 Y2 }. p. ~5 Y' }/ s6 i2 {, |5 H5 h- q  
# N  C% R) a. ]& L5 G; t( S然后保存: 
  }( R! K9 e  A8 e& G/ o% ?( ?" V  
6 J: m( @) h4 {' R- P2 E; ^, ^  
1 k6 h; R" U1 F4 r, @- j5 l- w6 O用FCEUX打开正常运行:) E- Y+ W; x! [ 
 8 @* U, e" Q( @! \6 r& ]3 X* | 
查看文件信息: 
3 A/ b! r. N2 }: x  
6 d2 G5 b7 ~: ^3 ^2 G, ]' P- A  
4 f+ f# F3 d) V6 y& w6 b接下来切页:/ Y# L; \6 B% @1 T& H 
先打开十六进制编辑器: 
1 J) r7 D+ {6 ?/ ? ! \) _% \; |$ x& ? 
拉到滑块最后,看看重启中断% R% ^# A: U: U( I8 F 
中断地址         中断         优先权 % l7 u9 T6 D  I: T( Y 
$FFFA         NMI         中 - K  |: O$ ^. N 
$FFFC         RESET         高  
* \" r' G: s5 d$ |& d6 W$FFFE         IRQ/BRK         低  
( ]4 ?5 V. v" C ; {$ z+ h( @  |6 Z 
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。& r: Y, I  A- P2 m( ? 
由此可知双截龙2的RESET中断$FF65。 
: |, f* a& i7 Q# ?5 g( z- a9 x接下来添加$FF65的执行断点:4 R/ j, h0 g% g) ~3 s& p9 D 
打开调试器: 
' b5 z4 K7 z; |8 @) O ) T/ F) A6 ~0 h) n 
添加$FF65的执行断点: 
4 J1 l6 s" s; L# V/ m1 i, h  
% t9 f# L2 W7 S  
+ {7 O: D4 n# L4 Z, A 
& m4 U* a& f* z2 f单击确定: 
. v. y6 S" X, {1 Q% ?# T5 R( { & F( p+ Z* i  W0 q4 P% q% x0 f 
2 P2 |$ w! P, Y+ I! t3 u3 H: b 
然后重启ROM: 
$ b9 f# a8 ^% x9 d& i ) {: b: p% v' v" Z0 k 
调试器此时弹出来:, u3 r5 h3 x! x! I5 [ 
  
% x- [: m  M, w4 ?然后打开Hxd,写一段mapper 4的切换bank程序: 
! v* q  ?6 J" c: Y" C! b, R9 b1 Z先看看mapper 4的说明文档: 
6 V# l: B! f3 O1 Y- p, OMapper 4 
# [# U, a2 _, @- _7 \' x5 z# _/ D) b5 ?. [) L# m 
$8000:  模式号! G( q, S* A8 p( t% v 
        位D0-D2:+ W3 t9 ?0 n5 D1 T  M 
        0:选择2KB的VROM存储体映射到PPU的$00003 G& @& e; @* U. C, w3 q 
        1:选择2KB的VROM存储体映射到PPU的$0800 
5 D* p9 _. j6 x+ [4 o1 C% f7 H        2:选择1KB的VROM存储体映射到PPU的$1000; o3 f' u' B$ }! z& f6 t8 _; d 
        3:选择1KB的VROM存储体映射到PPU的$1400 
% k0 i! `/ T5 l+ v8 @        4:选择1KB的VROM存储体映射到PPU的$1800 
# W' b4 E) h$ V2 [4 {; q2 D        5:选择1KB的VROM存储体映射到PPU的$1C00 
& W) ~# J7 d0 s  Q        6:选择8KB的ROM存储体映射到$8000 
5 @! S. S8 W& {5 A. `. P1 c+ g, W        7:选择8KB的ROM存储体映射到$A000+ X$ a* y  A2 n2 [ 
        位D6:8 p6 I) J3 g$ l! @7 \ 
        0:允许擦写$8000和$A0003 b% T, T) F& S+ D; P& w' P9 l 
        1:允许擦写$A000和$C000" f7 U5 S8 e  l 
        位D7: 
' n# j) m5 W* P        0:模式号D0-D2使用普通地址 
8 z9 k- X* _/ M: O" D( e4 p3 y4 K        1:模式号D0-D2地址异或$1000 
  C5 R! a" r! g 
0 h8 o, I( Z/ M# @2 J$8001:  模式页面号 
6 _; w7 k9 N6 ^; {4 I1 @        写入一个数(00-07),切换存储体到对应地址- o. b# n; p6 D% \0 I" o, Z 
! h5 l* i5 w* f9 q3 J 
$A000:  镜像选择" g4 `6 ~0 j/ |7 d" v, @: A* m! o 
        0:垂直镜像 
6 y4 V9 o' e' z  F$ O+ z! a        1:水平镜像 
' t# W9 n8 O+ k  Q$ J 
- s. O3 X' @0 e/ f$A001:  SaveRAM 切换 
' f0 R, Z0 Z: O$ ^        0:禁用$6000-$7FFF 
# s2 b9 W- x: q" F/ S8 ^" [, B        1:启用$6000-$7FFF 
- \. y" f' W/ a4 V6 a5 g4 ^ 
# d/ |8 v# g+ A$C000:  IRQ计数器 
: Z& M& g3 N2 E0 P        IRQ计数器的值存储在此处7 ?  [: l3 x, x8 {  H4 k2 h 
. D8 l9 j+ j  V3 N( o1 x 
$C001:  IRQ暂存器# ]8 ?, I# o) I+ n# ?( W4 A! b# o! y 
        IRQ暂存器的值存储在此处/ r1 K' u* C  C3 L0 Z9 E 
 
; t6 Q6 L( a1 l7 l; v2 o) a  I: c$E000:  IRQ控制计数器08 x2 N8 `8 a; U  ? 
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ7 Q7 ?+ V) G. _8 |2 X/ s& B* r 
2 _3 I( `6 p& P& p 
$E001:  IRQ控制计数器1  `  F) b) X3 p+ P+ y% t: Y/ e 
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)# E# R: [! k6 @2 Q 
. @, m; _# ~7 d 
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序: 
1 @% ~3 G# g$ U( }* q9 g48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68 
' g, }5 j1 I0 UPHA      累加器A入栈0 t6 k3 z2 d  t$ U7 j( v2 `- w/ F$ _ 
LDA #$06    设置切bank地址为$8000-$9FFF 
/ [5 c/ @, H% DSTA $8000* R  ^* m( m0 v6 C 
LDA #$0E    将第0x0E号bank切到$8000-$9FFF0 o/ G2 J$ ~: i# d 
STA $8001 
& r6 S+ l$ g4 g- mJSR $8000    跳转到子程序$8000* E! S, N# k' Z8 u 
PLA     累加器A出栈 
+ ?5 b- }7 @. }4 |! W) a 
# y1 e& }' n! s4 D( U为何切换的bank号是0x0E呢?2 g$ ^$ Q& @8 G4 c& Z9 s& R 
因为在扩容ROM后,文件PROM结构为:- d* U& j5 g& { 
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM7 ~$ H5 {& n& K, n  h/ ^1 R: P$ ] 
 
( N) _7 _3 i0 [: c" |# n2 QMapper 4切bank一次是切8KB,那么文件结构就是:. L5 v. G0 n+ b% a/ d 
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)$ G9 N2 k2 h1 t3 v& K% ^3 L 
因此选择扩容的第一个空白bank就是0x0E号bank。 
& f- u( _, ~- `- }. q6 \3 v 8 N( F  q( L  H, X 
) v, J! I% D7 ]: u 
然后去调试器找RESET中断中可以放下切页程序的地方:+ s; j( V- w0 n3 f' k( s; w 
首先长度要小于等于自己写的切页程序。 
/ |" M* I* ^5 H  P1 t可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方) 
' R! e0 e! t" J5 M! U  
& x5 f7 A) `  r5 o5 ~然后跳转到$FF7C:! A% C; c- P( ^$ c! j5 z' Q 
  
1 P2 I# D- F. n( |3 n+ ?" Z  
! j8 R, B4 d  i1 a6 {+ P6 ?, i$ Y/ m  
7 Z- g( O# Z, t' Q* ?$ j& U 
' S5 P* m! b6 D复制下可以被替换用于写切页的程序: 
, Z/ p# ^4 n+ P' B; o先选中,再复制: 
6 T' n# o' T+ w$ X- j! c  
2 s+ k1 Z6 v: W  
" J* p! G# c* K  I6 h  |8 @在Hxd中新建一个文件,把复制的数据粘贴上去: 
5 j; P) _! R2 x; N $ J0 j- |) T: ?& I 
 - L* k: Y- j; d6 e9 Z 
  
: G3 B1 X( }% a" T3 f5 w" j$ |' V: D+ w; D" u  H; Q 
然后回到十六进制模拟器:( }) z+ j3 V" i9 d 
转到$FF7C对应的ROM地址:7 F) O) L6 U5 }; I8 F/ J 
  
6 L" t* o& Y  ^+ w " a: `  z* x; Q9 M0 ?: U" k) G5 w 
然后复制Hxd的切页程序,粘贴到这里: 
7 C, ?, u! D; {6 {8 y' A 6 v; m9 G' J4 G/ v  ]; { 
  
/ c% k6 S' N( a" {* T. m/ J8 z  
4 B7 `) o8 f1 l/ Q- R3 s没有覆盖的用EA覆盖: 
7 W1 A- M. s6 i  [  
, G4 Z. ?. E# k- r7 _打开调试器可以看到变化:% E- E, L5 y: p; Q 
  
8 U8 Y1 B' V, S6 R然后添加$FF7C执行断点: 
# Z. I/ F  ^" Z# x! F7 f  
8 v% J+ l0 T$ }7 m+ h: d0 j7 o$ w; u ; e" F9 u# M" q9 Y( S2 J% y 
  
4 U5 o; K3 A' ]: K/ k单击运行:# M  K  r3 h8 g* |: _& Q2 T, Z 
然后程序在$FF7C这里停下来了。7 T4 O) p9 B' ?2 n, k8 ] 
 ! s7 o& F) ?* w4 |7 _. q 
然后单击单步进入慢慢跟踪,直到跳转到$8000:3 Q, }; j- ?2 e( m$ N5 c: R 
 7 Z& c, r& x; M& p1 s; j3 J 
然后打开6502_Simulator: 
( E8 z" H( g0 ]& A, O, x  
, l( G3 m- k, U再打开我写的数据搬移程序: 
- C) K2 {7 p7 U; S  Z: t  
8 m- ^6 n; j7 W  
, E/ v; h. p- N! ~  b然后修改对应的数据:: T4 t" z0 r7 m* K 
程序开始地址:修改比如$8100就修改为 .ORG $8100( t$ \/ R! D$ t% B! s- ?3 c2 h5 j' r 
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70$ A0 R. N1 S( E$ y: _ 
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82+ z" A0 y/ V8 I1 Q0 f' [) e4 u+ G 
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91 
- M+ H# v" k- @% D7 h# i" J也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。 
7 [* m# F# _5 z+ ~! Q如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。 
  ?% _% f3 K6 N" e中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。 
- q! e$ \0 \+ U8 m. ^后面的不用管。 
! u9 S3 Q1 z3 W  R 
8 L5 |8 \2 G, y& `3 ~" W4 P/ p( g/ R, D& s设置完数据后单击编译: 
1 U; U+ x- _  o5 u0 G 1 B. w- `( a! O0 k/ {% y8 f 
然后保存编译文件:$ E7 L1 Z  S3 ?, M7 ~) v 
 7 {* {9 x: i7 f2 [6 k  A 
选择二进制方式保存: 
" C5 U# Y- k7 \4 G% | ! }5 g; G0 c$ e3 d 
 $ t$ O7 N6 ?! w* _ 
用Hxd打开保存的二进制文件:* R' E% m. }! A& z3 G 
  
7 I- g2 I, p0 {/ ^& X9 A  
' U: S' g% ]# ~, ^; X- \跳转到设置的程序开始$8100:/ n& Z' m/ }% d  x2 U% Y 
 / Z7 K; [1 C! i2 N' H, d 
 9 w' g" N1 g! f$ L2 I' c5 b 
  
2 g/ B. _1 H2 [/ q回到FCEUX,转到NES内存的$8100对应的ROM地址:, K& `9 ~3 M9 t: s; T 
  
. m) e$ {, ]1 x; j) e' O  
% L8 Y& P# p2 m* E( D8 L- e   z7 M, R5 t; I! r& ]9 w; _ 
  
! z! n5 W) r: r- E: ~, n4 s : \& {$ N7 `" d) f0 h 
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:- F' A( G0 g+ }% K5 i 
 . v0 J& g3 X/ Y7 y: _" ~ 
  
1 t0 Z5 {, c' z, q  
+ h0 N* P0 I4 G4 R) i0 S: I7 Z然后转到NES内存的$8000对应的ROM地址:& k1 M. i: m" L) s! |& D 
 % S$ a, b  ~' v" S1 |2 ? 
 3 K: F3 ~% @! s4 p' G6 V. C 
 - V1 c' B3 q0 q; d5 A4 X 
  
; r0 ?* v* x6 S, i$ `8 `写上如下程序:8 [4 n) L! |  x/ h; q, G) T 
A9 80 8D 01 A0 20 00 81  + a& T& z8 Q' w% N) @, w 
LDA #$804 P& B. V% Q" R1 _; z! a/ l 
STA $A001 可写方式开启SRAM0 X$ @: r+ j  f3 `5 ^4 d 
JSR $8100 跳转到子程序$8100 
" D- {! v- h0 E0 F0 b  z2 a . h- T, N8 b4 @ 
然后把Hxd里被覆盖的程序复制过来粘贴在后面: 
) u) y3 j1 I8 O' y( i6 K# m7 a: v   k' l% e- U, p8 y 
 1 R! Q/ `& s7 n% }+ S( y; n 
  
5 F9 f  {. ]  |' ]* L: R; E末尾补上一个0x60: 
0 v+ a& }% o4 p# iRTS 子程序返回( S$ F* Z" b, G$ Q9 H7 S* [- K 
  
2 `% b6 w3 B" b( c4 g: b  N( s然后单击运行,ROM音乐响起,正常运行:6 ?, B2 V3 R' R: ? 
 % _- ^9 C* O* b0 d* U4 [7 `+ U2 H 
  
. t3 l/ Z" l) D! f然后转到NES地址$7000:; Q5 Q+ v8 T/ Y% |# m' S 
  
+ B6 n; Q+ G% E; o  C% y  
2 Z8 k% m- Z) U0 ]  
+ e* n7 v. N  i  V  
* n( k9 ^7 z0 z# J   |+ o1 b9 b7 M- K+ i/ M- J 
可以看到,$7000-7FFF都被复制了一片数据。# r2 b4 I* C9 t0 }$ G$ P 
测试没有问题,然后保存文件:" b2 t& A' _/ D" B2 ^ 
 2 ^" ~2 W7 ]+ Z; L+ P+ J$ \! u 
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。 
& _* _/ T7 e4 T# L6 L9 j" {后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。 
3 ]9 A8 p! Q: c, d! K |   
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册  
 
x
 
 
 
 
 |