EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
- `! K$ a0 \4 w6 |/ M; Y1 L4 A, c# o2 C3 k5 T, d5 z) ?
[FC][SRAM扩容教程(Mapper 4为例)]
( O* y- ]9 T0 c7 o( H8 u
$ k* V! C4 f8 }时间:2017.4.28- C1 y5 ?1 w) p' e. I2 Y* U
作者:FlameCyclone1 ]) s$ }- \$ G! [0 ]! y5 h. ^
工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator- e' I; v- S$ w4 e* R
ROM:双截龙2(J).nes# ?# M4 g% _( F8 m* \5 W+ A
适用:没有使用SRAM的ROM
6 G8 C+ l, Z& n
# L0 c$ x  k5 G2 t* w. T首先用Hxd打开ROM:
7 o* L: i# Y) K: [: c4 T5 P- b8 d$ x+ p3 s% a
然后扩容:
! @5 m$ A0 R' [% G1 M2 k$ G& u. y: W' o. a

3 \% b' f& q8 V' g8 M" m' X0 r
, J+ O5 w3 V9 r* m8 J5 Z! @) Z- Y0 N- y* h
+ Z6 c# W8 b  b$ d
5 G7 A& \4 j% d0 _5 {

) B/ m  V7 b: [( A( O: q1 J$ H/ i* i0 @9 j  Y6 R8 n" m9 p- j2 G

  Z8 I: _  T' b8 ?* Y4 b3 b先看看任天堂产品系统文件对NES文件的说明:
+ J) r& i6 V) ~; _NES文件格式
9 _6 S4 b! K- @3 t) ].NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
7 t3 w3 M$ p! }3 n& P' _# H" T8 x偏移         字节数         内容
% b! |; A: y3 @- g7 c6 l' t0-3         4         字符串“NES^Z”用来识别.NES文件 1 ]0 \  j3 J7 o# E4 m2 P" o
4         1         16kB ROM的数目
4 E7 T( f" Z& h. B' _# n5         1         8kB VROM的数目 # b) O/ B( |$ ~! x
6         1         D0:1=垂直镜像,0=水平镜像 ) d, B4 v  X# w. A6 X/ {
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF 4 V/ I  {& D; e4 R" H9 Y3 u
                    D2:1=在$7000-$71FF有一个512字节的trainer
) C; f& b1 T. B! v! `, Q                    D3:1=4屏幕VRAM布局 * A( k5 e! `1 k/ b
                    D4-D7:ROM Mapper的低4位
( x. e2 Q/ Y+ _7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
5 i- R8 Y! v/ N+ O  S2 X6 w* L                    D4-D7:ROM Mapper的高4位 : f( l. q/ X3 ~* o
8-F         8         保留,必须是0
' h: y8 n7 ?9 h3 e16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
/ \* I; M7 \) V; a-EOF         8KxN         VROM段, 升序排列
! S! P0 W1 `% N9 E- a  G8 O' Z+ ?3 D4 N9 N& Q% [, t0 p4 [. D
然后知道这个ROM有0x08个PROM和0x10个VROM) Y: I, O; T6 _2 H2 M+ h
接下来扩展PROM位0x10个:6 L- ~: `; t: E* Q8 |5 b7 D
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
, h$ C8 r0 Z  I3 {; }6 K  e/ h7 G' K
由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
# k$ k' M, d+ s3 I; B最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。
# [. F' _  X, C: e2 a* {: x9 e- c于是可以得到双截龙2的是:
: o8 a" x' W* ~) ?! {  u# r; O# @- g(8-1)x 0x4000 + 0x10 = 1C010。
4 {3 O+ I! u8 |' {0 e+ {+ V" I; V然后跳转到1C010:: O5 o2 d3 f: l$ y5 ?0 I8 M
( I' a3 o( T1 V5 U4 N. c% e

+ S5 P# {# H( I9 F$ O: I" A3 \3 v0 ~9 c
然后插入0x20000字节的FF:
6 J& c+ j3 |  C  L9 ?2 S& Z# f, B* A. r1 g7 F+ Q5 ?

9 [5 o5 j$ y* T! k/ L6 l  n; {; r: o6 b3 w
然后保存:/ x: t* C# G3 i# `7 Z

$ [* K* o4 J/ N6 C+ B9 B# G9 D
4 l  T& v, c6 [/ ?( L用FCEUX打开正常运行:7 ~$ e2 O" J( ]+ h6 [, W
) a$ O: B% H. y5 e$ R+ Q8 _
查看文件信息:9 j9 s- g; D# V+ G

1 @  }5 |# k$ l- j$ p8 P: e) v6 B0 y  M
接下来切页:
: k& ^& l( E3 K5 \先打开十六进制编辑器:4 w6 I  f  J6 ^1 x! S; _6 L
4 j6 a) L7 [) C% w( X
拉到滑块最后,看看重启中断
9 M8 z9 q7 m4 [. N  R4 Q: s中断地址         中断         优先权 6 S3 C) _: C* `4 V% |# F
$FFFA         NMI         中 ( X3 h: Z" d; Q8 B* o5 T
$FFFC         RESET         高
: J3 t! \* S- `$ ^$FFFE         IRQ/BRK         低
: H, M7 @, H' E3 h
2 L- t: y' b8 u+ d4 qRESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。
) @4 J, _% p. X; Z由此可知双截龙2的RESET中断$FF65。1 Q$ ^3 {- t. C3 ^
接下来添加$FF65的执行断点:
% n- X$ _* U  a) a1 ~+ c7 P打开调试器:: ^5 y0 H2 I5 @8 T3 U

- Y6 t: x0 N# S$ U& K4 }/ k添加$FF65的执行断点:
# ?  a; p2 F. r5 u' |9 l/ M/ o: j" F! H5 w6 t7 W: ?2 q& G9 p) t' f
8 o7 f! G( ?5 `! a. W% R
7 C# p4 v! f; g% _
单击确定:. B0 B) J& O  f. [4 o$ G

* R3 f1 f* w0 w6 r# |; p
' l1 a8 S2 l! Y  q" L然后重启ROM:
/ n2 [9 }4 p7 v8 o$ E5 V) Y3 N5 Q3 M" {" f; }9 n
调试器此时弹出来:4 d" v( j7 h& |, b4 }. @

; h8 D) f1 \. B5 A0 B$ z- F% p然后打开Hxd,写一段mapper 4的切换bank程序:2 j: F2 |+ c7 M& S5 `
先看看mapper 4的说明文档:( M0 J$ Q" Y+ B3 y& P3 |6 h
Mapper 49 c0 M+ W( S' j- B% n0 h# [. t! h
9 T% u+ k' B' c; O, f
$8000:  模式号
$ j" p1 E/ P: c6 |$ U( T        位D0-D2:
! M* n! S" c' l7 O! Z6 v6 y, p        0:选择2KB的VROM存储体映射到PPU的$0000
: f* L- i3 ^& ^+ E$ y        1:选择2KB的VROM存储体映射到PPU的$08007 \7 Z+ N) X) l' s4 L+ t
        2:选择1KB的VROM存储体映射到PPU的$1000/ \- ~# n& A! f8 o. y9 v
        3:选择1KB的VROM存储体映射到PPU的$1400
2 ~2 G2 {" P7 F  B        4:选择1KB的VROM存储体映射到PPU的$18006 Z. [5 ~4 t" d, v6 d1 M" v7 Q
        5:选择1KB的VROM存储体映射到PPU的$1C00; M7 Y( R) g( t
        6:选择8KB的ROM存储体映射到$8000
2 P4 @( e5 O& m2 S: S3 u, e        7:选择8KB的ROM存储体映射到$A000: z% R1 J& X! B" V' D2 I& O
        位D6:9 R7 ?! S: K, |
        0:允许擦写$8000和$A000* ^5 m8 B7 [0 [4 Z3 Q+ n' y
        1:允许擦写$A000和$C0004 |2 z/ v' Z9 z% ?  |- S) c
        位D7:  Y8 h0 n8 i! e7 ?9 v
        0:模式号D0-D2使用普通地址
6 `9 y' ~- E! n6 E        1:模式号D0-D2地址异或$1000  I0 s$ K: [4 Q( {# a2 u9 F) R

& P; x8 Q: V; A8 e$8001:  模式页面号) g' g; m8 E0 _( U. |
        写入一个数(00-07),切换存储体到对应地址' t( V: i7 |% z8 ]# s3 m9 U# }
5 N4 l2 w  h' ]- w* _: o
$A000:  镜像选择7 G( i+ o: `+ J# I1 x/ t1 ]
        0:垂直镜像7 L3 F/ R" s  f- ~! B  R
        1:水平镜像
, ^/ V$ V. J! N  Y
+ a+ k' W  O( U) V1 f$A001:  SaveRAM 切换
6 o: w# r/ w8 Q' J0 l        0:禁用$6000-$7FFF6 T% F; C1 F% m) `
        1:启用$6000-$7FFF! `$ A& l. n6 \0 |" }" q

. L& O7 b9 n& l, }" i% Z$C000:  IRQ计数器
" [0 [( ]- G5 t6 l        IRQ计数器的值存储在此处! _0 \- y9 T6 p5 b$ Q) N

! b( q) ]" h( ?8 L$C001:  IRQ暂存器' [' q- c3 W( e- Z& q
        IRQ暂存器的值存储在此处
9 k: V1 x- P" w: v6 `5 Z2 W
9 z$ H5 Y: s. P& I/ M0 \$E000:  IRQ控制计数器0' `/ t! X3 u+ }5 g+ Z0 j. @# m" `8 O
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ0 _0 o* G, G$ o# I' {4 U5 b. @+ {; h" t
  D! J$ ^6 n. k" B% H* V+ Z
$E001:  IRQ控制计数器1
" e5 Z* k8 x6 T+ q; k        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
* ^" O4 }6 S; e" J1 I
$ `1 S! @9 N" t) Z; M+ q. I那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
4 U, z! ]  L( A8 K8 D  k48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68/ L: B# G" J2 I6 X( M; A: \% k/ ^
PHA      累加器A入栈
. |6 S4 A1 }. {9 S  ^LDA #$06    设置切bank地址为$8000-$9FFF
4 R( a  w' O9 B& _- M' }! Y7 QSTA $8000
9 k/ z6 S+ G" ILDA #$0E    将第0x0E号bank切到$8000-$9FFF
. F+ ~2 D& o: w$ ?7 ~7 }8 }9 kSTA $8001; C$ ]# a/ q; L: G
JSR $8000    跳转到子程序$8000
6 U, H, C, t% z2 O1 ZPLA     累加器A出栈
5 ^- O6 Y: ~" w$ ~: ~
  B% {9 D1 @  a8 C3 N. q2 w/ m& ]为何切换的bank号是0x0E呢?
  ^$ c# D2 Q% _4 Y, m因为在扩容ROM后,文件PROM结构为:. F( V: v, U! G: Q9 b
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
) j) y& ^* A& W* V0 u+ b: \
/ x3 ~  ^- c* C+ @/ j5 _+ U. L+ EMapper 4切bank一次是切8KB,那么文件结构就是:
# ]2 I) J4 U' H" b0 S! a0 B原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
# W& w* d" ~1 O: U3 K2 {' p因此选择扩容的第一个空白bank就是0x0E号bank。
5 K2 ?. B! ~6 J, a) j: k1 c5 h+ z7 L* F5 s8 l' s0 X* v  G' ?

# }1 ]: M8 I7 `4 [: y然后去调试器找RESET中断中可以放下切页程序的地方:
6 w/ A& h/ h: B, F& Y首先长度要小于等于自己写的切页程序。
1 e" X+ \: T. P( {, U可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
- Y0 L7 \6 e; q1 e* v* G  }& W1 F' ^# [: ?: |
然后跳转到$FF7C:6 t% Y& G+ p+ D
6 d/ R* o1 R2 N2 v+ b* u

8 F' \/ D9 g# t2 n$ \+ }0 L
, P# G& R9 E* t, F6 `/ k
0 q6 O$ y2 C. P8 P* G复制下可以被替换用于写切页的程序:0 E& F3 g' U9 u8 y5 i9 s
先选中,再复制:
  r+ i2 n7 G9 B7 G$ h2 R
9 ]5 \+ K- e2 Z: m& G5 |
& S& \0 n) M2 a% H8 ?! i4 n在Hxd中新建一个文件,把复制的数据粘贴上去:
: F% `* x  r5 @- Q0 l$ z  M, K) @/ z( t+ P' f- E

% I* K# S6 k4 `/ R6 W: _$ Z! b' ?" ^5 j2 K1 e

; r  p7 l/ N  l然后回到十六进制模拟器:* f, ~; Y# c4 \7 v. j# E
转到$FF7C对应的ROM地址:# ?: L  T, h# Q* X
' C8 n& X: H8 M6 D  d
4 }" s9 K* z) G/ P5 C( m
然后复制Hxd的切页程序,粘贴到这里:
0 C9 J9 h  z8 |, c- v2 Q/ d+ b4 E2 T* c* e; L

3 M+ R% }7 _) X, J) u* s/ {% v6 N+ J5 R3 u
没有覆盖的用EA覆盖:
# w2 Y* N% E( j2 |& r. I" Y, s$ Y% l
打开调试器可以看到变化:* {. B* X6 X  v' O6 j0 g# ~4 p, ~
$ {! k) X5 p* H/ a% l& ^
然后添加$FF7C执行断点:
3 {: ~/ x5 q6 d2 G* X, O
" j% s  O' w) y: N) _5 F
, ?; L# q2 p5 n! c
: P/ E# T# `4 K: V& F" i8 X单击运行:
$ i' v* @, [* U- l# R# l然后程序在$FF7C这里停下来了。
+ ~* N1 U% \$ g5 n4 q% a, h. h0 T& \% M, |* ?; y9 \+ Q
然后单击单步进入慢慢跟踪,直到跳转到$8000:
5 U% v9 v0 Q, b6 R8 \
6 ]% D" w3 n* E- i0 a3 H, }5 ?. {* |然后打开6502_Simulator:! p7 [& Z. g3 v0 Q: F. V
$ C6 Y! j9 r/ S( x
再打开我写的数据搬移程序:- f4 h+ V+ R2 T8 x- u  n
( Y9 A/ f+ E/ V$ h. J& g# Z. }

6 n, ?6 i- |) x2 J; M然后修改对应的数据:
3 J2 M1 A# Z& G程序开始地址:修改比如$8100就修改为 .ORG $8100  d/ R  F; _7 E& I4 f- n+ v; ]0 u
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70- ]2 O' q5 v7 F% \6 ^
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82! S' R) Y8 V+ E/ p( f! C; ?
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
" X# u7 Z" O/ ^. |$ [也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。
8 {  B+ s" U4 e( q$ p' J如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。3 ?- I: V  m; [
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
$ W7 M0 M' ~" s: W# K: K/ t$ p后面的不用管。9 p, m& Y3 z# K; Z0 s
2 }1 f# J9 O: o1 K8 ^/ }
设置完数据后单击编译:
7 n+ B1 D" s6 q5 l8 X5 L6 e
% @2 u/ [5 F8 y. V% n# ?+ W然后保存编译文件:
1 H4 f! l) d- n, \5 a
5 E7 }' M$ \- v; ?选择二进制方式保存:
3 k6 `* D+ m* D; R; B. X8 ^
7 C3 h+ P. D# O1 ?
; N! q3 [$ C6 A: H) ^: Z' k/ X" k用Hxd打开保存的二进制文件:
! W; a1 d' O! b/ ]
: P1 Q) X% ~. N" D  a- H4 q
3 }1 A# C6 T+ [跳转到设置的程序开始$8100:9 g+ u# m6 h+ m  [. H
( w! j* F8 B8 L4 E7 d

5 W; B+ Y  i- T6 S& u' d- n3 }/ `/ H& ~
回到FCEUX,转到NES内存的$8100对应的ROM地址:  k3 U; x: V2 Z7 }5 q

* }- n( N% Y5 I* o' Y2 k) d
$ j( W* \& a$ Z0 S4 |& Z( h, w$ o) `+ I: Q" G

; i% h3 S6 C& m% `+ g
0 ]; P; R6 O5 [$ k, o# O然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
4 Y+ b7 ^# [, F* L1 f3 {! b- V6 M0 V

) y* C) f# J! U, _0 C) Z$ r+ W' p6 d3 Z! {0 p' K
然后转到NES内存的$8000对应的ROM地址:
. Z! A% y1 A; K# j8 [* t8 l2 D- M  D: d; @0 E  @* g, P2 y& B* ^
- {* K4 t4 U7 E, A% Y+ M0 [% v
$ s8 D% P% k% ?  j( U
! X9 M4 f8 X) i5 v  H6 a2 k
写上如下程序:
; Q) q( i; v# Y, p8 |. RA9 80 8D 01 A0 20 00 81  % u( l* g2 m9 o' B
LDA #$80
& ^# Q( z& M3 R% MSTA $A001 可写方式开启SRAM. v/ x$ q. A1 r/ u* H6 X* k
JSR $8100 跳转到子程序$8100* d, ^. u4 \8 `, d% W7 E
) h7 O9 n; {3 h* ^/ h' o' s
然后把Hxd里被覆盖的程序复制过来粘贴在后面:
. u- a) f4 L+ I6 F8 @. N7 H" J/ {+ w/ ~9 m0 q
4 L: J" f; W8 Q% L
7 k) r  e6 u) N- j3 ~8 T) B6 k
末尾补上一个0x60:
( l! I( m) {* Y3 e4 rRTS 子程序返回1 I7 ]3 Y$ C/ @- Z& l2 o

* K/ y+ B8 i; m9 ]. Q然后单击运行,ROM音乐响起,正常运行:
7 u; d7 L9 H" j# Z# p( \' m7 s- k7 A2 {  z

9 F0 Z& Z$ W' F' B. J2 ?. S- X& b然后转到NES地址$7000:# _. ]' }* T: _; l8 X

2 t( f9 q8 H9 g# G+ _, f0 ^8 N; }3 U+ K5 {4 ~* |9 u# W
5 R( R8 J% }, X6 J  S- ~! p' }9 A
/ i/ G! K6 X3 R) i+ `

/ i8 H6 z9 C# E2 L3 G# l7 D. O& Q可以看到,$7000-7FFF都被复制了一片数据。. n; n4 j8 H, c: N- o9 }
测试没有问题,然后保存文件:( c, e0 s3 @, g0 p
9 y5 Z7 s5 p2 k
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。3 E, Q6 N9 Y! H7 h% y( N# ]* q: I
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。) e- m- A+ ^4 B# y
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1157 天

[LV.10]以坛为家III

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

签到天数: 1198 天

[LV.10]以坛为家III

发表于 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 | 显示全部楼层
好东西,看看
回复 支持 反对

使用道具 举报

签到天数: 142 天

[LV.7]常住居民III

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-5 08:56 , Processed in 1.078125 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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