设为首页收藏本站

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 ( `/ e2 u+ l( v+ {

/ t! D& u2 t$ P% M4 ?4 {8 ^[FC][SRAM扩容教程(Mapper 4为例)]
0 u! {. U3 I) O
8 a) p. q! D6 |; _: e% B时间:2017.4.28
! a. M) Z+ U- ^. r9 W7 V作者:FlameCyclone
' c% x& O7 X$ P; S  e工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator* N; W! N  n+ p* S7 j& Y
ROM:双截龙2(J).nes
9 H. S5 b3 ~- @4 [4 p; s适用:没有使用SRAM的ROM, O6 K2 a( v4 W+ ~) D
+ Y6 z. d) I! V
首先用Hxd打开ROM:
) p+ e: B  K5 Z$ G3 k! a% Z
* |6 C- T) A& X- _& `然后扩容:
3 E& X+ H% V2 n9 Y  C* Z5 f, n; \/ @6 {/ ~

; ]* U+ j1 S5 H' _" s- k3 q% z
$ a/ I0 K) U' Z; P# D& s/ {9 B: v5 c+ b
6 c" e& o& {5 D, W4 {+ J7 G4 s1 C* g. b8 M, L' M! c& \# l
; ^: v, M- Z) p) w% C% ]( a

# U, E% p+ S; N0 z0 y: d3 r% [7 N! {/ q3 M' A# X0 B+ e5 m
6 y( e% l+ Z' A9 k* a
先看看任天堂产品系统文件对NES文件的说明:
& j$ z3 t' F' Z/ V1 f2 Z& E1 mNES文件格式
5 D: b" {& ~5 g; A" C/ b% c0 T.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
/ O" G$ W) J- m偏移         字节数         内容 ( N; T( W( k  V6 E* H0 y
0-3         4         字符串“NES^Z”用来识别.NES文件 . X  Z; x# |! ^6 d* U
4         1         16kB ROM的数目   \7 f1 t5 a# V' U- b4 o( b
5         1         8kB VROM的数目
/ k$ |* J% Q+ M2 N& }5 Z' H5 D4 M6         1         D0:1=垂直镜像,0=水平镜像 9 H* H& C2 E; \( a$ o- p+ }! K
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
" B$ B3 Q1 a% r) t: @                    D2:1=在$7000-$71FF有一个512字节的trainer
# }# t( ?2 T( R* E                    D3:1=4屏幕VRAM布局 1 x# {2 r6 J3 \% D; E+ z6 b9 i6 k
                    D4-D7:ROM Mapper的低4位
% E! M" F  j- v% `: `& @2 e7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
( M0 h6 n8 |+ p2 B  x5 U0 g                    D4-D7:ROM Mapper的高4位 * @4 C7 v; F/ X3 B3 b( i3 v
8-F         8         保留,必须是0
; H, Q" A# W9 Y0 L  c* c16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
3 X* ^6 N+ c0 [6 j-EOF         8KxN         VROM段, 升序排列 + G. C  y( n' K8 Q

1 }) a+ M2 ]; ^4 N2 A然后知道这个ROM有0x08个PROM和0x10个VROM
* ]7 Q! C: O& O2 J7 W8 V: ~接下来扩展PROM位0x10个:
$ J% H/ E  M4 m% ^/ F8 g1 ~先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
5 ?" }& o, i* |2 e3 c+ `" J
$ m; x, N3 M& s* `, a1 G3 |由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:/ A  h0 ]% U* F) m, V! I1 k
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。" [" D$ v  Z# o. s7 @2 ~
于是可以得到双截龙2的是:5 h' ^& j9 T* Q3 d( g! {
(8-1)x 0x4000 + 0x10 = 1C010。1 ?$ F) f+ u! w- W) _# {
然后跳转到1C010:7 k- H1 R/ T' c9 c' S! }, g/ ]

$ s" u  s5 e+ u, ?: \' T, u1 M' k2 P1 R* F. x, _
0 k9 @7 m8 Y4 F' m2 ^
然后插入0x20000字节的FF:
5 k1 M+ v0 ]! L8 t  D: X
( Q7 \$ }& u! F" w9 S) o) W  y; H. L/ g3 e

$ }" i1 F, @# {+ B/ r1 f然后保存:
8 ?% H0 v# O6 ]7 ~9 N6 `9 Y4 d, N! k. Y  j; O& e, G9 i8 K& `  Z! r
2 k4 I/ @5 M3 J9 S+ X
用FCEUX打开正常运行:9 @2 [/ b, g9 H4 z' i  L0 K

9 F" Y2 d0 D, }- J6 j& }查看文件信息:3 O) w: h5 l5 k) s- V

5 l3 F6 _2 U$ ~% y4 ]* f& F8 C$ R
: _  t* ?+ R- f+ ^1 @接下来切页:3 W" ~% G( _0 g* \
先打开十六进制编辑器:5 {5 k' C$ W* u
( e2 U, G3 @3 q* H
拉到滑块最后,看看重启中断# p. p: x, C5 J! I. K
中断地址         中断         优先权
# K( F& V5 L8 s+ o' v- j9 C# K, Z$FFFA         NMI         中 + D2 o' v6 f. A: K- Z: ]* b6 B
$FFFC         RESET         高 ' S0 j" d/ T4 y9 G8 G# c- n
$FFFE         IRQ/BRK         低 # a- X9 P% ]$ E, |6 t

) k" N, {& J7 S* p+ N- xRESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。. Z* K" l1 U4 i
由此可知双截龙2的RESET中断$FF65。/ m4 Q2 l& k  D: S6 R0 c: \/ n
接下来添加$FF65的执行断点:9 E$ i$ [3 `4 q9 _' q0 j5 R
打开调试器:6 U( ~3 f2 P. T; W" z! q0 ?

+ n2 `/ S, ?% j' s2 S5 u添加$FF65的执行断点:8 Z% r2 _8 f& j+ S+ O& T
9 \8 ?# i- N- t( b# n: }( h. {
! h' Z5 V. ~$ n

( [4 Z: V3 Q8 }4 O! k; C2 ^单击确定:5 Z! U/ J: y( L8 V. Q

& T5 L8 j& N: n1 A4 E# a) H' L9 m0 T: i, e; O0 Z4 b  \
然后重启ROM:
% V) x% j  f0 q4 P! W  ^* w% k2 ]
( m. F; b% v$ v: {5 b8 Y调试器此时弹出来:* e7 j* S* j% P/ G0 K' u) M
/ @, c; H  h# L* [3 w$ w0 r7 \
然后打开Hxd,写一段mapper 4的切换bank程序:
' B1 R1 {, s% u+ S先看看mapper 4的说明文档:
$ d; d  S+ ]# g5 W! }Mapper 4
9 Q$ h( d0 @$ Q+ _( x3 B& k3 p( Z$ z# ?6 h0 P
$8000:  模式号4 g) W9 N* b6 t2 _) o& C
        位D0-D2:
, d/ @$ ]! c: K5 \2 p        0:选择2KB的VROM存储体映射到PPU的$0000
6 R7 L, i3 ?/ ~, r7 j$ [        1:选择2KB的VROM存储体映射到PPU的$0800
" r' }" K3 z, h4 _1 D* J        2:选择1KB的VROM存储体映射到PPU的$1000  _0 Q8 G: X) K7 @: b! Y
        3:选择1KB的VROM存储体映射到PPU的$1400! x5 H  w) `! c4 b% o& v2 @1 l0 ~
        4:选择1KB的VROM存储体映射到PPU的$1800' H7 Y9 `" I) F3 R& I0 K8 N
        5:选择1KB的VROM存储体映射到PPU的$1C00) b0 p  S  A0 R# P
        6:选择8KB的ROM存储体映射到$80008 G: q5 O; S" N2 Y8 |# Y$ b
        7:选择8KB的ROM存储体映射到$A000
  d' H5 L8 p& Z        位D6:
. P" R+ k" ^4 ]# [# j/ `        0:允许擦写$8000和$A000
0 L2 p) S5 H7 B$ r  E        1:允许擦写$A000和$C000
. u7 x& B3 l: S        位D7:/ [) H$ e0 D' [: Z  y0 f
        0:模式号D0-D2使用普通地址  o. J3 `5 J5 p" A. u8 K# O% u
        1:模式号D0-D2地址异或$1000+ N. ~0 Z, v2 B- L4 V

! F" I8 i7 \6 V: o8 e7 v! \$8001:  模式页面号
9 |. P* _( ], A5 p: w6 `. Q        写入一个数(00-07),切换存储体到对应地址4 A% ^. i% L& c- G3 d
4 {8 ]# f  B5 P
$A000:  镜像选择" v: Y& _' G2 f  M  [9 G
        0:垂直镜像
: n+ t# _3 h; M$ [: n        1:水平镜像  B: K8 _7 R' P
; X, S3 A; M8 R, p7 r
$A001:  SaveRAM 切换" P: m( e" a( f6 q& i" k
        0:禁用$6000-$7FFF
. ?5 K9 A7 k% n3 w        1:启用$6000-$7FFF
5 F: U% t6 k/ u/ ^4 q# n4 G6 O$ {/ }4 q3 \2 k
$C000:  IRQ计数器; R# m( B7 w/ s" r. Z* H
        IRQ计数器的值存储在此处' h  j& C) s5 T6 ]

$ i+ {6 z, N7 l; S0 D9 S0 {3 `3 f$C001:  IRQ暂存器
/ J$ W$ K5 k4 Z+ o' [$ z( `: c/ v9 v        IRQ暂存器的值存储在此处- l* q/ h+ B' e) L% j6 d' i

- Q) m( v- W, J7 M5 E# ^$E000:  IRQ控制计数器0
- Z. w9 B* u9 D. Y' _0 p        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ0 S3 C( v, D& c4 q/ K; m6 h
4 V3 V+ B& R. b! H" P
$E001:  IRQ控制计数器1
; V: Z* H) v3 K; m0 p4 K        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)1 x+ A8 G: r# _1 e0 d

: y. H8 Z0 c, l% u- t% H: c1 Q那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:: q  o7 b) e/ |9 L
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68" n* e9 B9 J) A# O, f
PHA      累加器A入栈
3 E  e1 f2 u+ g1 A# xLDA #$06    设置切bank地址为$8000-$9FFF
, a* E! V1 n1 [3 G0 G% b) q9 hSTA $8000
5 V; ], t7 O' O, w, TLDA #$0E    将第0x0E号bank切到$8000-$9FFF, C1 e% t  e6 s) X3 V
STA $8001
+ }0 I8 }! X* D) D  V  P  b4 N" kJSR $8000    跳转到子程序$8000
" V" i" z5 ?9 ?7 Q; m7 }PLA     累加器A出栈
, B# ?1 E) W: K: N" R5 W& \
3 }8 }' `# ~4 S, d; N) M( Y+ B为何切换的bank号是0x0E呢?
' E& @# d3 \. e7 @因为在扩容ROM后,文件PROM结构为:1 T" E0 I0 |2 t9 I
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
& N1 w; u* l, `. F1 {0 J; ~+ ?0 `/ u: M) X4 T) z6 Q, ]
Mapper 4切bank一次是切8KB,那么文件结构就是:2 g5 q9 C4 M' J7 ]' E; m
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
5 a% ?$ E. U, I4 i  K3 \; v因此选择扩容的第一个空白bank就是0x0E号bank。
+ c7 T+ @& n. G
$ r; l9 F  Z, R
4 D9 l  F4 P% m8 r. A$ C$ Q然后去调试器找RESET中断中可以放下切页程序的地方:2 R2 Q1 L3 r7 o& p7 F) K% d6 T
首先长度要小于等于自己写的切页程序。
0 k- o1 B# m3 Z/ C" X可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
, V' i5 R+ `- l4 _. [
/ Z$ N7 Y' Y+ X然后跳转到$FF7C:
- D2 w- s/ j1 R; `/ P  f
# m$ i  ]- f+ D5 v6 h% z( E) |! r! b& s% o6 @2 n6 A# L

$ g( \' E8 o' q5 a9 B
& {+ K4 I$ R9 g: B复制下可以被替换用于写切页的程序:
" W) ^, F; a( H* q0 d先选中,再复制:
' I$ D9 x$ a* m% {  Z$ V: \3 l6 m7 P4 d( g6 i
( b+ Y& A0 ?' B8 F
在Hxd中新建一个文件,把复制的数据粘贴上去:
& S; z# L' o, G1 I4 M+ O+ H0 N
  B) ^* g1 }' L
* O4 `% r1 N, U* `5 E
2 s+ v$ L+ N/ P5 r7 K" k, M+ o" ]+ V! W5 j5 {+ j
然后回到十六进制模拟器:  `  d! d5 Z, G: ]
转到$FF7C对应的ROM地址:" c8 ]3 R9 M4 ^# b1 N) T' l

0 G8 ^; B+ E0 ^- ^9 t/ C/ a0 O7 d! w' ~, }
然后复制Hxd的切页程序,粘贴到这里:
) {( c- c4 c* E5 k# y$ z' \4 \# C# q/ u/ f

! g# Q, k1 R) c5 Z$ ?$ H9 Q, ?: ?, z% B8 v. j
没有覆盖的用EA覆盖:5 G! f& G/ Z  Y  `
8 ]8 H- [5 l' L  h1 @0 d) ^
打开调试器可以看到变化:
$ n8 S, t, ^, b: ]$ I
- t8 x+ s+ M3 ~/ f: h然后添加$FF7C执行断点:
- T! b9 z" i2 H# A
# {6 @* a  G9 ^+ E6 C/ b, t' F
9 R) _$ ~9 s8 c; \! t/ B2 o/ {7 O1 m) s2 y; `
单击运行:
) S* p& a/ G" w. D, p+ w5 v9 n然后程序在$FF7C这里停下来了。$ P2 C0 g' }( R5 p/ _* y4 @
' v5 K! T  z) o: N0 f
然后单击单步进入慢慢跟踪,直到跳转到$8000:
2 [3 n$ L% Y' q3 N# s% f7 Q2 p  P8 l9 {" B3 t" M$ b
然后打开6502_Simulator:& e' q) L5 T& A& r1 `

( P7 H9 D7 V1 @4 F% V9 D# }6 ^% v再打开我写的数据搬移程序:1 j( n* Q! E% S) o& l& `6 r
1 Q/ R  ?3 e5 g9 t" O. `, Y
) k, S4 Q3 q6 r, e) o+ b0 {! |
然后修改对应的数据:( D" _. ~) a; A# N& @2 t4 s* i
程序开始地址:修改比如$8100就修改为 .ORG $8100
' N. ^; ]) k! j1 s6 T复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
# U8 E/ {# U* z从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82" l- C! n# o: U* ^0 f
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
$ f( n: u7 Q7 f" x也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。8 `5 Z3 C. D% f5 Z0 f
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
9 G7 o4 Q& |& s# i2 U) ^: h3 H中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。- s& F! t5 o4 _5 u' C2 b8 h! o( K
后面的不用管。
& a; l9 ~& t1 K- @8 r; f- I% Q" i& `
设置完数据后单击编译:  i9 f7 _& A) ^' q# }

4 D  b3 |+ o# I5 r0 |3 A然后保存编译文件:
% ^; l) {# V+ i! j4 ^# R* z/ C2 U! w) _; J
选择二进制方式保存:
$ w# C2 R8 g) N6 x* j% Q( ~/ w

( A2 O9 G' O/ `3 F8 u用Hxd打开保存的二进制文件:  ]3 o) Q6 c3 g: \4 Q

: ?6 u' y! X5 E! a/ C# }3 J$ M5 H& n
跳转到设置的程序开始$8100:' f3 |" O1 c5 F8 z
2 ~0 q& E7 m; \8 ]- E& F

. x$ V: }; f9 i2 |8 K+ @! i0 Y  P' P/ C
回到FCEUX,转到NES内存的$8100对应的ROM地址:& u2 Z3 v' O* e2 f
: u! c7 X, ]" ^5 {5 ^0 D
1 o1 ^( e$ r* ?. P

# |6 a* J2 z% d9 a8 J
: l9 T. C# ?$ {! w) A
6 e# G* k5 J3 C6 X4 s1 z7 X. `然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
3 h" Z, T( t- h, K7 {9 C+ P; C* q) U: }$ c* ?( r+ b9 h
9 w* P* _7 B* S: p! ^# P' m

& p) R# j+ _- d# z- I* j, ^然后转到NES内存的$8000对应的ROM地址:+ L) K6 H  X/ b

( K& L: E! F8 q: I: W5 }2 _
( Y/ N& G: X1 a
* n- b- o" b5 U" `
; F# V0 u3 b  ^+ ?$ i# Y9 s3 @; n6 W写上如下程序:' o0 c4 W. i1 Q+ [% A" V% y
A9 80 8D 01 A0 20 00 81  8 x. q$ U/ {  X7 i3 u$ X
LDA #$80
! _  m( J5 p, H$ F! V/ xSTA $A001 可写方式开启SRAM* t$ m. O! R+ D/ J2 P9 _: L) Y
JSR $8100 跳转到子程序$8100
5 Y% `7 r. p& r5 Q* L) T( B1 L7 d& I, C! |6 v$ f/ Q
然后把Hxd里被覆盖的程序复制过来粘贴在后面:
$ x) E9 B* F- c7 s
8 H, m0 [9 [: `$ w8 T: j2 U/ M  X: ~) d; n, {" O  y; Q! {; H
! F1 q  f- b+ Q! \$ ^8 |
末尾补上一个0x60:9 [- X7 F6 ^4 _/ Y: H( A$ x
RTS 子程序返回7 t) |7 Q1 l  @; |9 [
, E. o& ?% @+ S1 g* ?5 a
然后单击运行,ROM音乐响起,正常运行:
9 Z5 Q( a0 t* Q: Z
4 Q/ `" b6 H+ I" P1 `& l% k; q3 J$ S9 Y1 M; y+ K! ~
然后转到NES地址$7000:
; Z5 F+ B+ W( H( O4 Y* F7 {! P0 A+ _  S, B/ u

) ^  n# B- T/ W- f! ]" d! a+ Z9 c
+ A$ J# j& V  K; n
0 Q( Z6 C9 q6 W- _! `7 w) ^, K
可以看到,$7000-7FFF都被复制了一片数据。
7 @( @0 @9 A9 v) V测试没有问题,然后保存文件:/ k% E: f7 Y* z8 ^3 h" ?

$ x! \1 w  B  o+ ~9 \6 n* y以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。' h" ^9 N1 x) S# c. \
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
2 T8 o% a. |- z4 F4 N) [
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

×

签到天数: 2060 天

[LV.Master]伴坛终老

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

使用道具 举报

签到天数: 1184 天

[LV.10]以坛为家III

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

使用道具 举报

签到天数: 1613 天

[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, 2026-6-26 01:23

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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