EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 8 i6 J3 a1 X" v  N7 M% i, i4 W
7 _" T% q5 ]8 b$ ]( S9 @4 ?3 s
[FC][SRAM扩容教程(Mapper 4为例)]8 G6 I, F8 B/ T" r" ^1 U
& S+ t# m' L" G0 \$ M) o
时间:2017.4.281 S1 Z: `8 T& W  ~% O+ Y$ I
作者:FlameCyclone
" B* n* ]" r4 s/ \4 M. x工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
; Y8 e& c8 b9 \: l2 {ROM:双截龙2(J).nes: F1 t5 A7 a* J" o; g
适用:没有使用SRAM的ROM! H, r; b( `5 l# v$ V% q& j
$ R/ h7 d) E* q+ W3 v3 J# i
首先用Hxd打开ROM:/ N0 p: m& q0 Z  ^$ X

+ c& v/ {- L# }% i0 `& y, D  e然后扩容:
7 x4 l7 ?  M1 |0 ?  E
+ [# {! Z4 n' |2 d5 B4 m. S& O2 v/ ]8 U" }% E' s

" J, l0 U% K9 u- j+ i5 U# M3 \* R6 ^) l3 @0 D

8 D+ a; x* Y9 H  U  |
# k2 U' H; @$ r7 V' L3 B7 y5 I* k( K: a- S# A' x/ b: ?

# p4 Q* Q3 d% t2 V# r" K$ _% f  j- }. _6 F7 D7 T% ?
先看看任天堂产品系统文件对NES文件的说明:4 w& y* }: w" B$ O1 ^! I5 z. C
NES文件格式. d: g5 q8 J$ B% M
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 $ M9 c- `: T  ]6 N  ^
偏移         字节数         内容
* i3 _: Y" a7 w0-3         4         字符串“NES^Z”用来识别.NES文件 & }3 x9 Y% R2 O  x' P
4         1         16kB ROM的数目 ; M' {( A; A# }1 \* \8 S
5         1         8kB VROM的数目 1 ]1 t9 P' b1 o' n
6         1         D0:1=垂直镜像,0=水平镜像 % O, L' U8 j: M- E2 Y& ]: q. U) S. G
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
' Z8 b! _+ {. G8 h- R                    D2:1=在$7000-$71FF有一个512字节的trainer # Q; S# k1 N/ q8 Q
                    D3:1=4屏幕VRAM布局 ; w- ]2 E3 T4 }& G$ y6 e, m
                    D4-D7:ROM Mapper的低4位 , N6 A" @# |" C; u6 V( t
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
7 G6 P( N* b. Q, P5 f* U) J: }/ e9 Y2 q                    D4-D7:ROM Mapper的高4位
. I. ~, y! f% X' d6 M. P8-F         8         保留,必须是0
& v7 |: v: c4 t2 I- G+ l16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前 & u+ r2 a# }6 O5 `  @0 ?0 u' M
-EOF         8KxN         VROM段, 升序排列 & Q% G& O& i  A' m
; j3 @" h( Q# u/ j. A
然后知道这个ROM有0x08个PROM和0x10个VROM+ X4 D1 R* G# a2 ]; w
接下来扩展PROM位0x10个:
0 g3 m1 B# J! c" B8 G1 o先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
+ }" P8 b- O/ G) t& W( h6 M6 w7 v
5 J! d8 @" q+ }% W) g" k, h! v由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:" A2 o( B' t) v" g5 Z
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。+ {* m( f' y+ c/ _( X/ ^% f1 y
于是可以得到双截龙2的是:' x5 P9 C  Y6 g0 h5 C4 |, U  C+ M
(8-1)x 0x4000 + 0x10 = 1C010。. f! G4 Y' ?& _8 |$ z+ D, v
然后跳转到1C010:5 T1 w6 r, y0 h$ O
( ]) C; c. h# D/ [/ ^/ ?+ u: n; k

# {! D  p( ]6 b  E4 S
& P! [* S% T' q  W  I3 B5 O0 J3 |然后插入0x20000字节的FF:
3 V" t( q# s7 F! w" f8 b$ Z9 \! Z" z! x) B# y, P" O1 H5 R+ @6 W
2 H7 S  E2 l, z6 H
5 k9 e$ u: k8 b5 M" s4 M3 P
然后保存:3 Y3 v4 h/ r/ x7 t) r; b

3 k! }( O6 ^" C- ]* ]  W* M
0 S! R3 j# z4 G2 m  E4 h* s用FCEUX打开正常运行:, D9 h% T) ?3 M1 N& b
% k* R! U& d2 M3 x6 W
查看文件信息:4 _9 g* c6 u8 h4 t+ \
/ C2 }; W' V# k% j1 A  H! ?& S

# ^5 o; u, g7 @4 L: x  g接下来切页:! q+ X# F4 i: g7 Z2 I2 `9 I
先打开十六进制编辑器:
8 i9 j4 U  k3 B/ F
- ?- D9 R( h+ ]; P) N拉到滑块最后,看看重启中断
% ~. a# T! ~  w9 {中断地址         中断         优先权 / v! Z5 }* e3 f! w* \0 s
$FFFA         NMI         中 % c1 L6 m- Q' Z3 [# U
$FFFC         RESET         高
7 r* Q3 g" u0 b7 x0 Z+ J$FFFE         IRQ/BRK         低 . M% _/ ~1 d+ i8 F4 ]" [
' d* A( g& D% B& l% r9 ]( V; X4 d
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。+ t5 y% u( H! ]* e& _
由此可知双截龙2的RESET中断$FF65。
+ u4 {; w- z' ?接下来添加$FF65的执行断点:
5 y+ u; w+ I) `/ m打开调试器:8 W0 y8 D4 Z# G1 m9 q  F

* B: a7 p5 A( Z0 k- F7 a添加$FF65的执行断点:
$ F( h5 a" k2 N+ Z8 _# e' q& h' l3 N. X% t5 w
. t7 z2 Z2 I0 z8 z2 `  D
, o2 |: U) `4 y6 ~8 w5 A$ q
单击确定:
  K/ [% j9 U% p1 u* `  K  o' U' x
( V* W, j4 s' s8 u- q7 I% ^% ~7 V8 h
1 d0 o' t6 |& A, j然后重启ROM:+ u. [) W0 x/ _" E" o, M4 E" u" P
( B' ?! C- x& r/ H
调试器此时弹出来:
2 V8 y, M1 S' V" @/ E
4 [$ \4 X5 K* q  c然后打开Hxd,写一段mapper 4的切换bank程序:
# m! m3 k: R3 A" m2 {, X先看看mapper 4的说明文档:1 F4 Y  Y$ b" x" _& g' e* A
Mapper 4( B1 Y* C  y6 J: K: q! M: g

- s; U5 h5 y" [; U0 W1 P9 ]* y$8000:  模式号& j% Z$ R) L# T1 V
        位D0-D2:4 _0 U9 q" B3 T( m' E
        0:选择2KB的VROM存储体映射到PPU的$0000  `5 p( l0 T8 K; n. p4 Y5 E- f3 v+ n
        1:选择2KB的VROM存储体映射到PPU的$0800% T( m( B* `& ]' g# V- Q) C
        2:选择1KB的VROM存储体映射到PPU的$1000
  r" N! A* K2 Z' i" X& }1 ?        3:选择1KB的VROM存储体映射到PPU的$14005 f) z2 K+ Y/ \& J. P
        4:选择1KB的VROM存储体映射到PPU的$1800, s' n; R6 w- p* `
        5:选择1KB的VROM存储体映射到PPU的$1C00
1 @6 u& l) z# N( w. t: @' \! a        6:选择8KB的ROM存储体映射到$80003 A- A) Y1 c) [( J8 _: M8 d8 z
        7:选择8KB的ROM存储体映射到$A000, Y* ]5 t; T# K; d5 Z% [6 s  v
        位D6:
# V4 R4 i4 n/ i2 C; ]: R# \        0:允许擦写$8000和$A000
  r( I+ o7 r% q" I: X0 T        1:允许擦写$A000和$C000; W3 D# @, Q) w
        位D7:
1 D6 E' ^# ~, z! \2 b4 E, x        0:模式号D0-D2使用普通地址0 j5 u: A* y* J) p" l" _' S
        1:模式号D0-D2地址异或$1000
. c8 R4 J% V; F6 a' G: {% J2 F, x9 g2 @+ ^$ f3 J2 ]
$8001:  模式页面号
* v- ]3 T$ A4 y        写入一个数(00-07),切换存储体到对应地址
* X1 v& O  G& ]# O6 S$ ~
6 e: }4 M; S6 s2 N$ I$A000:  镜像选择0 N1 v" G) c+ K& g6 z! d/ Q' p$ o
        0:垂直镜像% ~) C$ K7 p) i% i0 A/ W# X
        1:水平镜像& c( ]6 C2 M, ^( l: v
) ]; V4 G! u8 z# f0 x7 p: r  q
$A001:  SaveRAM 切换
* V9 [& ^' Q/ S; ^9 m        0:禁用$6000-$7FFF
) p. a" f2 ^0 f, W: p        1:启用$6000-$7FFF
' t: B  r! Q3 S, R9 |  Q& T; J; @6 U1 q6 G, f, V
$C000:  IRQ计数器2 P6 s0 s4 w- V8 A( Z
        IRQ计数器的值存储在此处
/ g( H# s$ P. }- G. ^; d& m2 q# P$ E# I2 H3 p0 W3 l* ^8 O3 B4 y4 Y
$C001:  IRQ暂存器4 E0 S4 \8 y: a& e, ^' A+ v. O
        IRQ暂存器的值存储在此处5 I7 z. M* d, z$ T' @8 w1 L

8 y; \2 {# G: s( C/ B$E000:  IRQ控制计数器0
9 ?4 v" ]9 ?  ]; G% r( u        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ
/ I% D7 D+ F, v0 l1 ~* E  s/ g! S+ d
$E001:  IRQ控制计数器1
. |, x+ x$ D6 Y5 }: s" i        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)( q" S7 \6 z! |. S% u2 h

  ?4 Z0 c5 X, l- C' _那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:) t% n  Q4 a2 _9 C% ]' t
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68
6 u& i& t; T( n' E* APHA      累加器A入栈! ]' D8 F4 N9 V4 ?1 t& {4 H
LDA #$06    设置切bank地址为$8000-$9FFF
! S2 B2 ]6 n: b3 H8 eSTA $8000  i' V/ T" n; E
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
) T- `4 ]+ J7 @4 w- B, _9 R4 XSTA $8001- f1 S+ `& m) k/ q7 V/ P
JSR $8000    跳转到子程序$8000
1 t. i7 e; \+ J! cPLA     累加器A出栈" q9 l/ U3 \7 m; Y5 c& h
# @6 k) ?3 I+ W2 V- {. ^1 G/ n
为何切换的bank号是0x0E呢?5 D+ u$ d) f/ X# m) e
因为在扩容ROM后,文件PROM结构为:# m8 T3 S: C3 D5 l) g7 y* G9 F
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
" O( |  m' j  V: s. h3 e
7 M. M' \0 g3 X9 L$ |Mapper 4切bank一次是切8KB,那么文件结构就是:, a, b1 z. P4 J1 D
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
2 D/ B, G0 W! v3 F! u4 c0 Q) ~因此选择扩容的第一个空白bank就是0x0E号bank。
: U1 j' \' i, @
! e  O$ b$ ]% B! F. A. N3 d
* d; {: S; j' a* ]然后去调试器找RESET中断中可以放下切页程序的地方:
4 U' q$ @  x2 w+ y! E* }: t9 E首先长度要小于等于自己写的切页程序。
9 s3 @( }/ J: q8 h可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
  ]; s$ H" g# K- K' r/ ]& d
  P) y  j% \5 i6 e" q& L) A然后跳转到$FF7C:
7 g: Z% T" c: w; C" c3 P7 t: ^. D: P- B4 h" h

/ R* l( s! ]+ k. O
$ T8 {6 t$ S  T9 [1 o7 x  ?7 k1 {, }% @) C9 Z
复制下可以被替换用于写切页的程序:. R6 U  L2 G$ M% }9 h: h5 ?" v
先选中,再复制:
1 I0 j; K" ?. Z' F* j5 X5 c: O" E* K$ v, L

2 h& J% g1 K0 I/ e- u! s9 \4 D& D9 K# O在Hxd中新建一个文件,把复制的数据粘贴上去:
* ]  e: A( k# j4 ?% M+ c5 s( T. f7 e3 \3 X
# w+ V6 u: Y% J

5 v2 A8 Z& i7 b) Y7 Q
  q9 r% H% J9 m然后回到十六进制模拟器:5 C4 i) y" V$ P0 P5 }8 R! D
转到$FF7C对应的ROM地址:4 r5 e! I0 _+ l( x. Y

8 K" p$ G# ^8 x5 E) S: c! h4 H# v
然后复制Hxd的切页程序,粘贴到这里:
" S1 u5 l3 z5 V0 @3 G: d. i* {, C$ Y& x+ T# u

2 _/ f& h5 R: [; q7 k! [
8 I* }( g3 }+ O9 @) y1 O2 C2 {/ r没有覆盖的用EA覆盖:
9 ?! p! Y! a8 ^6 Q. T! u; i, x7 L# t1 b
打开调试器可以看到变化:
* `2 h! E9 m! d* x3 V# b1 a1 K  j# q$ ?
然后添加$FF7C执行断点:8 U# M/ l5 _6 T' e

% q/ ?* a4 N* G, O7 M# [
# l' x9 G/ K) {* O6 R2 ^7 b8 w0 |3 l5 e4 F- x1 s8 S* ?& ~
单击运行:+ W; R; l; n! x6 t
然后程序在$FF7C这里停下来了。$ n0 X. G' j4 C/ H7 o6 J( Y

  d+ `0 ~( m% P% z: l. \9 U) }2 H然后单击单步进入慢慢跟踪,直到跳转到$8000:; H- J1 H. X# a- X! k

$ Z# Y( o, Z' p' v然后打开6502_Simulator:
; _0 G% ?2 o% d6 C8 F
) T9 E7 ^; }) r( o+ [再打开我写的数据搬移程序:0 v0 D9 q( q0 Z' K7 `9 h
6 K4 g6 y* v# }8 o; j2 k; K0 z
3 [3 v  N3 G# |; s( ^
然后修改对应的数据:7 t+ m  _- A3 }* S
程序开始地址:修改比如$8100就修改为 .ORG $8100
6 ]2 i% O" U& P1 d* d2 a# N复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
5 h+ q3 s8 g8 H! I0 r1 p- X% q* V# e从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82* `! A+ u; o9 W3 V3 A
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91/ }0 F( D/ E7 R6 ]9 r# f
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。9 ^3 M4 u, r! h* ~( P
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。. S3 N) s* N  O6 @3 h' Z: e
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。4 V/ n9 t+ P) G3 d% l$ r9 Q3 |! M
后面的不用管。
  [( u( f% ~  Y- D' F" b
! l3 W: L6 }2 l* V7 }( N  m  X% ^设置完数据后单击编译:
0 o/ I$ s/ M* R8 h8 J8 h- u9 ]% r. o! I
然后保存编译文件:: R  s+ R5 \4 `- }# b  S% O4 k

" o: @7 f0 x/ ^# Q8 A% l; g选择二进制方式保存:" C$ [& t1 v4 r. y, K
/ ]% H. U4 m- N# Q* ~$ h1 t- t
: Z: u4 q! _& i- B: t6 w
用Hxd打开保存的二进制文件:
) K. W& S, s  B8 D0 O% [/ V
0 M, U5 a! j, m* h3 Y
9 A' H# d: U$ N1 d* i; U$ D跳转到设置的程序开始$8100:
! ]. n2 F$ A* ^: N7 A, S0 b  I5 o) P! T# r

5 i6 ~7 t1 [3 l+ e
2 i# B, `, k+ {0 \5 s9 v6 o回到FCEUX,转到NES内存的$8100对应的ROM地址:
+ C4 k  w* i& V6 A" J9 M5 V1 u3 u7 j7 [7 M

  x, L7 \8 S7 E' n& |; ~
, s% d' t5 }$ ?1 b; O# M; @* q1 t' h8 p7 _8 w" L5 m8 h3 d
6 @! A& E5 a3 ^6 G2 h
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
  |: |, X( A  l$ g1 U/ g
2 ~3 @5 F6 v7 B* B. Y
; Q* P- _1 h; {8 T* @( i
2 R: J& b; g! z, \+ c) a- o然后转到NES内存的$8000对应的ROM地址:
1 c! I+ J+ B! I8 x3 g% e* n* M
- q, r- m( P" W/ n/ O  S
" n! k" T1 I$ `: u- b4 v5 b6 y9 v; l' q2 [" E/ l+ x$ p

+ J/ ^" }" y  ?写上如下程序:
9 C/ H  g" Q0 ^1 \A9 80 8D 01 A0 20 00 81  
/ a' j& `, g5 h! u- _9 ILDA #$808 c# }% r  Q% f- g4 F
STA $A001 可写方式开启SRAM
+ W  j- e% M  y3 Y; NJSR $8100 跳转到子程序$8100
2 N7 ^- K; f( o+ B. Z
' n) w  ]4 [2 a9 e2 @8 S( [6 G然后把Hxd里被覆盖的程序复制过来粘贴在后面:
7 y5 W, t1 _7 T! x' }5 n7 Q' z. q
' W( O) u! A1 V6 N5 ^* _( N7 k1 q3 `

# j2 U3 D  X" q7 e末尾补上一个0x60:
) H% l6 k) D2 y0 K! L) ~RTS 子程序返回
# H. K" J0 w- B9 k" [5 j6 ^5 Y) y; @
然后单击运行,ROM音乐响起,正常运行:, b  {! t0 x2 j% h
- ~: f' p, @+ [' f6 f6 E
( Z3 x4 \/ t$ f- y
然后转到NES地址$7000:; }, y& ?( @  |6 k* J
, J; O0 {" W3 Q# Q; i/ t
. H/ u3 \, y/ w) `" {3 L

+ ]1 F( Z- [( w4 M
. K0 Y# a# k- r" u7 S7 T) x9 l- Z6 T, z3 _
可以看到,$7000-7FFF都被复制了一片数据。6 T) V7 B5 I/ j( ]
测试没有问题,然后保存文件:" a8 |0 m$ {) A2 ]: q
; j/ s4 S' m) G; X! Q/ ^( \
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
9 `# |- f# I$ q$ Q5 U5 a后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。; D" Z' Q! I( S# ?9 B
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1179 天

[LV.10]以坛为家III

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

签到天数: 1390 天

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

使用道具 举报

签到天数: 208 天

[LV.7]常住居民III

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-11-18 10:32 , Processed in 1.076171 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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