EMU618社区

 找回密码
 立即注册
搜索
查看: 888|回复: 8

[原创HACK教程] [FC][SRAM扩容教程(Mapper 4为例)]

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 8 Z2 I# ^" M7 d9 G8 L
  q. x4 q5 W# I: z  e0 d
[FC][SRAM扩容教程(Mapper 4为例)]
5 q1 {1 e1 w' h" X" ^
" K8 q; e5 t. k* e, }6 a9 ^时间:2017.4.28
8 Q: _8 A6 F1 J6 h作者:FlameCyclone. h) r( ]' n# F
工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
" Z4 a$ L. D4 R# I1 ]; FROM:双截龙2(J).nes
- p9 Y3 w6 z) j" V1 c适用:没有使用SRAM的ROM  _+ T3 t" H9 p' a) p
( Y0 z$ l- j1 x$ _
首先用Hxd打开ROM:' S7 \: r% c9 y" g
& y; S2 D" A5 d% h) }, r
然后扩容:' n; S1 B, c) ^$ ^+ H& N) m
3 f# s! }4 B: Z2 h% z* X% _+ W

3 y" g1 h: d4 c) [4 a
/ c/ Y0 ~& f! K* l# s
5 Q- ^& O/ i& P+ F% k- W3 s+ X
7 C. i  \" n6 z, W9 B% B$ `7 f, j4 z9 U: {8 u% i/ C: O' T

/ _  E7 d1 b9 ~
( S0 ]( b+ t0 F% h* z9 ?1 u" t. v$ I
先看看任天堂产品系统文件对NES文件的说明:
, _" ?. u% a4 v# f( o. i. JNES文件格式2 I! _5 w. |  f* g& l; Y
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 1 H9 C/ w9 Q4 d5 t3 t1 u2 e
偏移         字节数         内容
+ r3 r" M' n; ], f! x3 ^+ S0-3         4         字符串“NES^Z”用来识别.NES文件
* U4 h3 ?, s/ Q4         1         16kB ROM的数目 . h0 n! v' h( C* I
5         1         8kB VROM的数目
! d! P" @. o8 e* T: }4 L1 G4 x6         1         D0:1=垂直镜像,0=水平镜像 # u3 u& d0 C4 p. @, [8 a
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
* C7 ?/ N0 u" Q: a/ G                    D2:1=在$7000-$71FF有一个512字节的trainer
0 X7 f. R1 q, _' s" ~: @! @                    D3:1=4屏幕VRAM布局
3 U8 m+ Y0 ~" j% X. ^, w6 k" p! M8 ~                    D4-D7:ROM Mapper的低4位 " I& Z/ O3 w6 p" E( l' Z
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) # v& M3 c9 ?8 K0 D2 U  i" q
                    D4-D7:ROM Mapper的高4位 ( K" r) Q: I. R& K
8-F         8         保留,必须是0
+ v& d+ @: S. [$ m16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
" J! @6 J! I0 x4 R-EOF         8KxN         VROM段, 升序排列 + t% R0 f! d& d
5 ?- e8 M0 I8 A: D* J0 t2 `
然后知道这个ROM有0x08个PROM和0x10个VROM" z9 r! \& Q- u
接下来扩展PROM位0x10个:
% M. w% |# B. @0 S* _) s9 i先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
, `8 @6 @4 i$ N# l4 L
! U, c! i! S' A6 ]4 c5 x- l由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:1 U0 F1 u* Z! }3 i3 V
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。% t- d% F! H- L3 m
于是可以得到双截龙2的是:
8 H8 M* l0 W8 O: r# M; I2 ?(8-1)x 0x4000 + 0x10 = 1C010。+ o4 l9 P+ [0 ?5 n# u3 T
然后跳转到1C010:4 L+ l# `% Z/ ~% B! b

: J0 f; t% ]0 B9 j; ]+ I: K" d  _
3 j$ H- p2 U5 v4 a8 X1 G) M  y: v6 [" Z
然后插入0x20000字节的FF:
1 [& v1 V" V3 S$ e" @1 X* u" t6 x/ F* q

7 \8 r0 Y. v3 b2 q1 o6 A. G# z1 j$ S% q9 q3 T/ i
然后保存:5 z+ ^$ r8 h! q+ D- J) j8 Y

" c; L  [5 ~% m* u' x- G( l8 O+ n1 m/ ~, H! p
用FCEUX打开正常运行:
( F( I  `2 \8 S! M3 Y7 X3 s* G0 d; p+ r
查看文件信息:
7 M. W2 Y2 M4 ?8 y% e) S4 H$ l, \( f0 X* ~+ n" e) \0 e7 A$ `
2 @9 E6 f. q5 q( z/ R$ T
接下来切页:
0 h2 z/ U# w: H先打开十六进制编辑器:
* [1 }' G% S7 H0 G; _( M5 u( a# O" s1 H: C' w
拉到滑块最后,看看重启中断
2 W. w8 k( ?- C% c' H  N1 {) }中断地址         中断         优先权 : n0 k; @/ i. d0 F7 K% \( `: S- f: L
$FFFA         NMI         中
+ z& p: W# K0 C* m0 L' H$FFFC         RESET         高
  j" _( u2 h) ^( }7 j' |2 B$FFFE         IRQ/BRK         低 " u& t6 n) p& H/ c: E$ ]
, v2 U; O: d* W/ a' n# e
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。/ N* n2 W. m9 r6 r  G2 @
由此可知双截龙2的RESET中断$FF65。7 \5 U+ N# m& p( h0 z7 i
接下来添加$FF65的执行断点:
' V! g( u: E& a, c+ V$ u7 |打开调试器:
/ ^2 @3 H* D+ u! m! S# d+ k
) Y+ ~4 S4 d9 A  G添加$FF65的执行断点:
1 v9 M. T0 |# Y1 Y, Q) `& c
6 S8 U( V* L% D; h5 a7 P- [+ i4 O) F2 y' G. L* v8 y

( {! D! R* @+ D单击确定:6 S1 H6 e: u+ d* \4 ~+ M$ u# r" r% s
+ f" ~: m5 R: ]+ t. [. X

$ ~4 ^: R: d9 G- ~! X7 O然后重启ROM:9 f8 h/ R5 b8 R6 y" X) b
; C. W) r2 }( w) z0 F( R! u0 F
调试器此时弹出来:
( K% W( N4 D: g: r( t) d7 [4 ~. `+ t8 H+ U) ?
然后打开Hxd,写一段mapper 4的切换bank程序:& Y  b) O& I! c) C, y- B
先看看mapper 4的说明文档:
) N+ B4 m3 b0 _9 e9 b6 j7 CMapper 43 F0 W# |3 P; k8 _" D

# |+ O" z4 r/ m! R  n9 I% f9 O$8000:  模式号
0 M3 n0 ]# f7 ^; Y7 p. x& B! Y        位D0-D2:
& _+ X6 ]/ K  e9 M8 w9 w1 q        0:选择2KB的VROM存储体映射到PPU的$0000, D: t, O) e8 l, z' W2 y* K
        1:选择2KB的VROM存储体映射到PPU的$0800- @. m2 m) w6 j' P7 f
        2:选择1KB的VROM存储体映射到PPU的$10003 u2 T5 x0 H. S9 }& k5 U: y
        3:选择1KB的VROM存储体映射到PPU的$1400/ S3 A# T. @8 H
        4:选择1KB的VROM存储体映射到PPU的$1800
. C+ m2 U( n. |" K4 R        5:选择1KB的VROM存储体映射到PPU的$1C00
0 B* S4 g* A* |: t+ q% h        6:选择8KB的ROM存储体映射到$8000
7 l! ~( K  r# ^( x        7:选择8KB的ROM存储体映射到$A0004 x! `( W5 {& \; j( V
        位D6:$ Q5 [9 i" Y- i* Z- y+ E& x
        0:允许擦写$8000和$A000/ X) D9 d4 f9 @- ]
        1:允许擦写$A000和$C000, {6 J3 ^- U. p) ~' d$ m
        位D7:
) H% \: x1 F4 y/ A3 F4 T        0:模式号D0-D2使用普通地址
* G, {1 X6 u6 I' R        1:模式号D0-D2地址异或$1000
: A$ o/ ]& q9 A& v  e" [4 w4 g% F& ]* b% O  N. G
$8001:  模式页面号* S. ^* H. ]6 Z( r5 {5 j
        写入一个数(00-07),切换存储体到对应地址
- r/ m6 Y* L6 K- D% x
- M; i: X$ _8 \2 B: z* W$ e+ r9 p$A000:  镜像选择
! h7 n3 g0 U% K* H! _0 @        0:垂直镜像
$ t& `, s/ {& y4 d" R$ R3 n5 z6 \        1:水平镜像
5 y( z2 J* Q0 J' B1 ^7 `; u1 }% }* y8 _1 [
$A001:  SaveRAM 切换
9 [; U, y2 }& v( A8 F        0:禁用$6000-$7FFF
8 y5 \. Z& l5 p3 \3 B, \        1:启用$6000-$7FFF/ x0 @7 @/ L) Y

9 O3 ~7 _) S1 b8 B5 H8 e$C000:  IRQ计数器, \# T  |* I) a; \  N
        IRQ计数器的值存储在此处
9 @4 R3 e7 f7 _' L& ^9 [3 M" X7 E8 S- a5 W3 F3 A5 Q
$C001:  IRQ暂存器% k2 O1 R5 \4 c8 M
        IRQ暂存器的值存储在此处
8 M  k" ]+ ~0 f) Z* K, J2 z1 x
' G: q8 o: x8 ^8 Z$E000:  IRQ控制计数器0- L1 Q  A9 M. ?, {
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ
. G7 S6 I4 |5 `# }8 }- V* B
) ^. f4 `6 t% e* q5 W/ i4 o0 ^$E001:  IRQ控制计数器1# F/ I. y# L3 H$ w) U( b& M2 _) o
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)" q0 Q/ G* p2 O9 L# a
( c: }; A" D4 U' w( i* [& S3 D
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
  v* B, [* t# e( s7 {48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68
* W/ R8 l, ]$ A2 z' S" o5 i5 kPHA      累加器A入栈
( }8 i8 V- z6 J( s2 u+ yLDA #$06    设置切bank地址为$8000-$9FFF
- B" A- M* `9 ~. _0 K9 A. q' LSTA $8000+ V/ a' X7 a4 v& k* j
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
9 T: m6 ^2 p4 f0 G6 g2 USTA $8001) v8 W. F" d5 A4 p6 u7 @4 u+ u
JSR $8000    跳转到子程序$80006 V; T. [. o# b, U8 {- {, Y
PLA     累加器A出栈
/ f( m) i# l5 r) H8 \- n2 s9 o- `8 }5 V$ a% d
为何切换的bank号是0x0E呢?& n  y( d# M' G( K7 Z/ ?7 @$ G
因为在扩容ROM后,文件PROM结构为:3 Q6 b) X8 a9 N
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
' f( t% `* J# h3 w7 p
* {2 D4 a! L0 A- A. IMapper 4切bank一次是切8KB,那么文件结构就是:3 c5 N( K. c# ^: k: d' R+ J
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
7 y4 F1 W* l+ T# ^+ X5 G$ j: a因此选择扩容的第一个空白bank就是0x0E号bank。7 X" N4 X4 U. m8 {! H8 e
  Y: i3 F- z1 X8 |8 h  m
, x7 |9 L0 e7 F# Q# N
然后去调试器找RESET中断中可以放下切页程序的地方:
! Z1 q/ F( H/ U首先长度要小于等于自己写的切页程序。, j7 `. d' a5 A( ^) z+ h
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)0 I3 R; @: M1 Y8 u
& ~# K+ g- ^; a  R. j; y2 q
然后跳转到$FF7C:5 ^, b3 `* i1 o" d

+ B# \8 \6 z4 g3 O; o4 J4 y; x2 N, E6 Q+ }" z6 w4 S" Q9 [/ [& O
. N8 c* V; v4 M9 P2 e

4 `( h- J  }* E, O复制下可以被替换用于写切页的程序:
: I& e& `* c8 q, q" \+ F- l先选中,再复制:
" P( v, ]' n6 H* d- r9 c7 x; m  B, p

1 p1 \) J; \$ n) P在Hxd中新建一个文件,把复制的数据粘贴上去:  L' k7 v8 A' o% h- r# r6 f

) \/ F7 V" y# {. v0 w/ X/ \& v5 b$ n! N7 M9 @* e: C; A
3 D' v1 U% C3 P5 X; h" `8 _
  g2 A* ~$ S: l5 h! T
然后回到十六进制模拟器:1 g% T) M' t; Q$ x* `
转到$FF7C对应的ROM地址:
; N2 t+ m9 k0 ?% k* C
% Z* H% F, t" k) m0 h
$ a0 @) O( P/ e( r4 T然后复制Hxd的切页程序,粘贴到这里:
/ ~1 _( b5 M9 S% |4 t# m3 s# S8 x( d' }% s# x
( O  {/ a" X' y4 S! t6 J
! p) m9 B3 x0 Z8 @9 v" t, f
没有覆盖的用EA覆盖:. q  C  V4 t, R& h% z4 }7 x% t; C
5 Z1 q8 {. u5 g
打开调试器可以看到变化:+ m* ~' d  t3 l9 H; }
# Z; d# a  t/ h1 O% F
然后添加$FF7C执行断点:: P+ Z4 v- z' b7 M$ M- S4 q
# R" H+ ]0 Q* E7 v5 f* n

+ R; w5 P* v' s6 j8 A  K! c. |" I9 p$ e& |  i
单击运行:
1 Y8 L6 j6 v: j5 T" l然后程序在$FF7C这里停下来了。
" a8 V8 `1 `3 }+ \- @) q3 t! m* s) l! W6 O  e9 i
然后单击单步进入慢慢跟踪,直到跳转到$8000:
& s) @0 }) A& w2 l2 e0 }9 }/ E' ~! L, D- A4 X  J# R$ @; M% P5 }
然后打开6502_Simulator:3 a* {& w) A; y% K# _' s- W' M
% X7 J0 `) c! I2 b2 C* P- @& A8 e
再打开我写的数据搬移程序:
( N1 F- u' B% ]% N' a6 R
4 d9 _: m# `2 ]' P$ N7 c' Y' c1 z
5 z. o" X6 q6 j* T6 F+ f) u* f1 o9 v- Y然后修改对应的数据:3 R" Z( k* e! A% R. w# G/ U/ ]: u
程序开始地址:修改比如$8100就修改为 .ORG $8100! _0 T' X$ k) a
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
! r7 u7 C! X, B9 z9 W) m6 i5 @从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82" l- _% q# w& p" X& c$ L1 {
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
0 U$ \' z" p! ?* E3 @也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。
1 \; \2 R5 A1 o, i2 m5 O; t0 l如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。; g5 O3 z1 A* ^2 x+ b: ~- X
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。& J6 w: t! b) v$ h
后面的不用管。
5 S# G, |; P% a# ^: u0 y4 X8 r% i; E: J5 e/ e( z- ?8 G! @
设置完数据后单击编译:
, R( p4 y& l& V( }9 N  D8 b' \2 ?" q) W2 u
然后保存编译文件:1 l5 g. R* ?" e3 u5 g& n; g
) w/ d* e! [5 m% D5 w
选择二进制方式保存:
" ~: m+ n! R/ j  s: a9 t( x5 w" z! H1 Q9 ]1 @
: w3 H6 k. P3 W# g9 }* V
用Hxd打开保存的二进制文件:
, \  I8 \  K* \  E. Q  Q9 ^  j) S+ d5 a; R) \) a6 a( ~

9 T* A) i2 Z8 O% X1 d9 Z& z" b2 m跳转到设置的程序开始$8100:
, B% f7 r5 L8 D' m2 c7 [/ I* e1 g' U
8 _" V* s- Q: @% \. f! u% L" |5 q2 y
5 a3 c# ]6 U; K8 x
回到FCEUX,转到NES内存的$8100对应的ROM地址:
+ i% _) g, {- b" e  r' @. J# I! e$ D+ c6 Q
/ B6 Z  {: c9 K' T
+ S3 u6 O$ K, W: L) f
0 K. d( u6 T, s9 s. w+ W: E
  _7 e! }$ r5 |, |: k* M1 Q' m' U
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
* I; t) l0 b8 H! }0 m  p# N* x* q. T0 P$ }8 i6 g+ ~
$ R! a) o& K! |8 t7 w

/ K+ N+ {7 V- L3 B' b然后转到NES内存的$8000对应的ROM地址:: a! v# H3 |2 m9 }9 i

; r8 s& C/ E3 ?. P' I/ _% ~1 j+ }7 Q) e- h( d5 h% h# p( g- x
5 z& ?5 {0 {  o& f# [+ c
; i& K0 h! Y8 t& h+ b6 H
写上如下程序:* K' ~( c; G( a0 V
A9 80 8D 01 A0 20 00 81  
( i& ~* v: N/ ILDA #$80
2 c! F) M9 V& wSTA $A001 可写方式开启SRAM8 s! e1 Y& [) Z2 F
JSR $8100 跳转到子程序$8100. b$ _/ @6 W$ P- h; C5 `9 `% ~

" s" |7 L! z: K3 \! e5 C9 D然后把Hxd里被覆盖的程序复制过来粘贴在后面:
- L$ b/ [2 p& o3 \" I
4 M; F! O$ B( ]5 L6 j& I. }- I, {8 k+ y* i( p" n
# {* Q0 |3 C3 f7 x
末尾补上一个0x60:
! t5 f6 P. C3 j" @. W5 }RTS 子程序返回
1 x" k& `/ P/ U- V  Y, d9 c4 G# \
" r3 V/ ]$ b3 M- C* j& [然后单击运行,ROM音乐响起,正常运行:; Y" r+ n0 U1 |
) [. x/ n3 C9 ?* F. z( O; _1 Y

: ~/ S0 f- O/ K% H$ O0 `, ^然后转到NES地址$7000:
9 ~9 F" {& K# T1 M8 R$ l' z6 V! c3 p' [) D( Z

! k- ?* Z* X, K* G' k# _& M! T. H
0 t* T# T* Q5 h* X' L) y" Z3 M& Z1 n2 [# H; `, |& e# h
# B( g3 Y4 G/ H) s
可以看到,$7000-7FFF都被复制了一片数据。
% g4 K9 \6 p$ Q" y1 r) n6 X$ t测试没有问题,然后保存文件:
' I/ c: L( Q6 ]) n, d9 e# i, U0 N2 P5 m& u( c7 z$ X' I
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
! N5 r! `  i; f0 c/ V; @: s3 U6 r+ x后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
$ ]5 E' @. T) @* I" f
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

签到天数: 2060 天

[LV.Master]伴坛终老

发表于 2017-4-29 01:07:47 | 显示全部楼层
好多内容没有心情看了、、、、、
[发帖际遇]: liujunbtx为灾区捐款 6 个 柠檬. 幸运榜 / 衰神榜

签到天数: 1183 天

[LV.10]以坛为家III

发表于 2017-4-29 21:00:16 | 显示全部楼层
火纹外传扩容就可以汉化?

签到天数: 1629 天

[LV.Master]伴坛终老

发表于 2017-4-29 22:24:54 | 显示全部楼层
非常好的教程!👍

该用户从未签到

发表于 2017-7-25 15:03:46 | 显示全部楼层
{:4_104:}来学习学习感谢分享
回复 支持 反对

使用道具 举报

签到天数: 30 天

[LV.5]常住居民I

发表于 2017-7-27 21:47:15 | 显示全部楼层
看一下楼主的教程,非常好。
回复 支持 反对

使用道具 举报

头像被屏蔽

签到天数: 5 天

[LV.2]偶尔看看I

发表于 2018-2-8 21:36:34 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

签到天数: 57 天

[LV.5]常住居民I

发表于 2018-10-23 13:32:34 | 显示全部楼层
好东西,看看
回复 支持 反对

使用道具 举报

签到天数: 208 天

[LV.7]常住居民III

发表于 2024-3-22 21:01:24 | 显示全部楼层
进来学习。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|国治模拟精品屋 ( 沪ICP备15012945号-1 )

GMT+8, 2025-7-17 19:18 , Processed in 1.087890 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表