设为首页收藏本站

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
* m2 v! \  b3 l2 S2 E0 H  j$ |: W7 [5 s! D! k+ \
[FC][SRAM扩容教程(Mapper 4为例)]3 f  q# R: u+ ?

& L$ H4 H' R7 x时间:2017.4.28; h' S4 x# `1 d( h0 B/ A
作者:FlameCyclone$ B% F+ E& z. B/ W* y
工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
4 L: c! x+ d& Z9 u# O( cROM:双截龙2(J).nes
1 i/ q5 n. i6 F' H/ p+ V适用:没有使用SRAM的ROM+ \7 x! \7 W* K, H$ t
9 \+ K9 E, l+ D" r
首先用Hxd打开ROM:
, E5 N* h9 c& ^# P7 J; k( |: Z! r- v- Z8 p
然后扩容:
7 s" v: n) p' S& R, C  J& c# r) l$ \7 ]
5 ^; }8 [+ P: x& }. X; o, \

3 F6 g" o% v2 R' B% c" {8 S& D4 i! `; z+ R1 M

6 p' M! J- @4 P- G" y' w% I. B1 T: L  r( B
& {. X" B/ v) ^6 r4 z* j/ e/ d

, v9 O" p1 L3 D4 M: t: j5 @
- Y. n2 d& i( x6 h' n* X先看看任天堂产品系统文件对NES文件的说明:
+ o6 F, Y" u5 B2 j: `/ `4 E) nNES文件格式
8 A7 q9 d& b3 A) R.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 5 k8 d7 U1 I- b5 X, `# I
偏移         字节数         内容
$ _& v8 s: J) N8 d+ t) u0-3         4         字符串“NES^Z”用来识别.NES文件 ) Q- d, G  M- i2 r4 {$ e
4         1         16kB ROM的数目 8 c  q# K  Y) ^
5         1         8kB VROM的数目 5 D7 W4 s- k. y
6         1         D0:1=垂直镜像,0=水平镜像
' ]  u0 ?6 r' g- Q                    D1:1=有电池记忆,SRAM地址$6000-$7FFF 5 \4 K; |6 S: U
                    D2:1=在$7000-$71FF有一个512字节的trainer " D2 q6 n( c* m$ o. F$ x4 {
                    D3:1=4屏幕VRAM布局
" o1 T8 t  V+ _3 H& ~                    D4-D7:ROM Mapper的低4位 ( z$ I" n, O9 Q1 |4 {
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) - n0 ^3 T, Y8 w5 ^
                    D4-D7:ROM Mapper的高4位 6 ?% F8 `2 `* x8 r; |6 d
8-F         8         保留,必须是0
3 `  T1 N4 L5 X7 |: O16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前 9 g5 t2 ~) M- v3 Q" f; {8 q
-EOF         8KxN         VROM段, 升序排列 ; ]/ L! ?* W' C8 l, v

8 E" z, @9 m( w. n9 x& s然后知道这个ROM有0x08个PROM和0x10个VROM5 m# E+ M  Q# a2 h- Y
接下来扩展PROM位0x10个:
. y0 S* i, P8 @" m) @3 e" s: s  V先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
2 ^* G# Z. n% V$ R- d
3 |1 s$ a1 \# A/ o& s3 X由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:3 k9 h+ w2 u+ h$ [
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。& J) H8 _: A+ {. z2 ]. z! q9 w! c9 Q
于是可以得到双截龙2的是:
: I0 E& A- b4 M: G5 W2 p# p5 B(8-1)x 0x4000 + 0x10 = 1C010。& r. I3 t7 T& d" v6 k! u6 P
然后跳转到1C010:
$ [& `7 O5 d& q& j$ {5 x  h/ i) R4 c, \( w9 k- _0 T/ P

! v; |& ^9 O# D9 }" \
, R. x7 S; a; x. T: a! R然后插入0x20000字节的FF:
) x- E1 J# [  l7 J& X
: f8 m) v; L* y( ^5 D; _
4 V! p1 g) D: ~6 `8 u0 y8 g. R0 X+ E$ Q* k, M% @8 h4 [0 S% B
然后保存:
( ^4 L( ]8 T; q2 z' t
0 ?) n; @2 p, F1 d3 v8 M6 O- O- U* q
用FCEUX打开正常运行:
7 y$ R- r1 y* V$ Q7 s7 E: |$ X8 T" F; W3 ]0 J  v) h
查看文件信息:
: P9 R9 x; f5 ^# {  o; M/ N& E8 F: `6 t7 }) J; l- L/ y

! k7 l* ]0 l" b  d* O  Y! J+ w接下来切页:
$ c6 S7 O7 a+ ]' W先打开十六进制编辑器:
3 O$ Y4 r& v" s" ?7 D
- |  j* M. K+ m: t. |拉到滑块最后,看看重启中断
. T( j3 G2 u7 c/ {8 l% `# T" X8 k中断地址         中断         优先权
9 ?$ ?% J5 u/ F9 K; M$ a- `3 c$FFFA         NMI         中 * f  P( B; |% S9 N" `
$FFFC         RESET         高
  @0 g, c9 S* F. V- h; e" [) h$FFFE         IRQ/BRK         低
  f/ h. f" Y* b# P4 ?6 K
( {4 @) l# _! XRESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。
7 \7 U3 j6 A6 Y. [! }; I由此可知双截龙2的RESET中断$FF65。' n! p) E+ E1 c5 y7 m( N- |: D8 ?
接下来添加$FF65的执行断点:
, n, w' y& P7 P2 V1 j0 t4 }9 T打开调试器:
4 Z) b, U, L6 Z* ?9 z) u9 }% `, T$ t$ P% B, a9 K* ~# g
添加$FF65的执行断点:" k) y; X0 J; R1 t

. L" N, ?7 e  }% l4 ~7 {' c
2 M! b8 {- Y  ~" l; ]9 T
8 b8 Z+ m  j$ e6 N单击确定:' z3 R$ W) |4 Z2 m! q: x6 t- n

4 U( e8 w! n2 D2 H5 ^0 e" S- S/ {; A9 P% `. r9 U
然后重启ROM:
# m8 t* u* J6 W, `& c( G( N! f* d0 ~) V- O) q2 I
调试器此时弹出来:# d% w( U5 k/ M# y1 ~
0 _% A3 T' Y% v! J% O9 a4 J3 C, g' b
然后打开Hxd,写一段mapper 4的切换bank程序:+ ^. v4 D# [8 m% i9 u9 O
先看看mapper 4的说明文档:& I; L# @: `7 U
Mapper 4
9 X( e' n, L9 J* W8 }' p* |
; p( Q! S1 V! v; ?& ^$8000:  模式号
' v3 ], H: Y; H, ]4 q        位D0-D2:8 x4 _+ Q9 x. I
        0:选择2KB的VROM存储体映射到PPU的$0000* w! z/ Z0 }% c  n' z! G. ~. X
        1:选择2KB的VROM存储体映射到PPU的$0800& n8 [8 f0 E3 B3 U* a5 F- g
        2:选择1KB的VROM存储体映射到PPU的$10002 p7 V$ n3 I6 N# C, N  V; v4 Q
        3:选择1KB的VROM存储体映射到PPU的$1400
0 N& o9 Y; \7 F6 N+ y. d. j        4:选择1KB的VROM存储体映射到PPU的$18007 O* q9 [2 B& D! K$ M2 {
        5:选择1KB的VROM存储体映射到PPU的$1C009 |1 w: ]5 X% Y
        6:选择8KB的ROM存储体映射到$8000+ D+ \; k- q8 Q4 Q7 S. O* o" f2 @
        7:选择8KB的ROM存储体映射到$A000
0 I" T% Y* e5 a* m* T+ R4 s& f        位D6:( e6 w; p3 H5 u9 J5 r  F4 V
        0:允许擦写$8000和$A0002 J  E$ Y, R! K; q+ R4 M5 U# }- G
        1:允许擦写$A000和$C000
& d# P% R! Y$ }+ N* Z( f/ N! p5 r        位D7:' c: J0 B/ _0 d$ o
        0:模式号D0-D2使用普通地址
) e# x: A& b) M2 ?        1:模式号D0-D2地址异或$1000* j* Y3 @2 y/ E
& c8 ^" V9 \! g
$8001:  模式页面号
0 i0 {& f" P6 }        写入一个数(00-07),切换存储体到对应地址" ]* [. |, e5 I/ w8 V- Q: w
4 j$ w/ m, e. z/ p; x
$A000:  镜像选择, v& u) e$ a% R$ [; m0 D7 }, a
        0:垂直镜像4 r4 V9 J0 G8 L( B6 a2 F
        1:水平镜像( V) W0 H) y; ~' l4 |  p
  x" r: }$ Y. d% }+ r( }
$A001:  SaveRAM 切换& K, K2 d( a( x4 ^2 ?: S' P
        0:禁用$6000-$7FFF
( g/ X+ }" J- M# @% |0 X; d        1:启用$6000-$7FFF9 Z, d" J. k1 Z) i  P. e
  I2 _3 _2 V2 v; p, U
$C000:  IRQ计数器0 |  `' Q1 s( `& {- D0 K5 R, `
        IRQ计数器的值存储在此处
( \9 e$ j6 `/ g9 l8 C8 S9 Y( h6 t
0 |0 @0 k& @/ V: T6 M$C001:  IRQ暂存器8 S- [$ m. I) U# ~% T4 [3 S7 f
        IRQ暂存器的值存储在此处
+ H" J0 |* ]4 j8 a! Q0 O: r7 B+ R
' ^9 J2 \- `' W! p: |! O$E000:  IRQ控制计数器0
( x. V9 ~/ [! Q' q' J        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ
% v* s; W+ K" Y
3 P  \+ A* F9 _$E001:  IRQ控制计数器1- A9 R! H. W3 G0 V3 Q* c+ M
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)- t6 U9 g8 i. ?* j
  U" @: ]8 |7 V4 A: A' ^
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
& n3 i( w+ y( p* a2 Y48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68! V" J7 @  U$ ~$ g, V0 E
PHA      累加器A入栈: [+ T, @; t+ h: F# c4 [
LDA #$06    设置切bank地址为$8000-$9FFF% ^9 n( l5 l* c# s8 n- ^
STA $8000
4 A0 O  G1 F* m2 n3 c4 p  GLDA #$0E    将第0x0E号bank切到$8000-$9FFF
5 M2 _+ @$ K; {0 bSTA $8001: y( }. Z, j2 `, p- A
JSR $8000    跳转到子程序$8000
0 P, l! w. j& K' N4 c3 _+ f8 g$ xPLA     累加器A出栈
# f3 u2 j; ?" I6 K
9 N- Q1 h4 f  P9 H$ u9 ~7 o为何切换的bank号是0x0E呢?7 Z9 k4 W$ D! z! @
因为在扩容ROM后,文件PROM结构为:$ ~( x# `0 f) T: z
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
0 {* i( S+ o% D2 R* g# [$ {1 S* l, |- m+ C2 h# b' K5 _& M: |
Mapper 4切bank一次是切8KB,那么文件结构就是:& ^3 N; a8 {& w- B/ t% `
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)5 Z1 W& O9 x+ n4 [3 ]9 \5 j; [
因此选择扩容的第一个空白bank就是0x0E号bank。
' X( w' z- }1 [6 e& L" G4 s( p. ~

" w4 Z5 A8 w, D! g8 w: v然后去调试器找RESET中断中可以放下切页程序的地方:' r9 Z5 @3 n1 X6 B; `
首先长度要小于等于自己写的切页程序。, X8 ^0 z# W2 D1 f7 s. b8 B
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
" [' J6 k8 [) G8 n& Z& y7 u: I4 U8 k5 ?. v
然后跳转到$FF7C:
* h3 E) j/ y1 u+ t0 ^/ f) G/ u' h7 P
; M: ^* @2 u, K) k1 M( \

$ _# i7 Z9 |! @$ g8 L% J
  ~! C/ x- \' y复制下可以被替换用于写切页的程序:
) T/ d- n* C% I, J" S" r先选中,再复制:
  \! t; ]/ ]: v9 h7 j
7 [' i: K- e# O
, q, t+ d- g7 ?8 o/ _# ]在Hxd中新建一个文件,把复制的数据粘贴上去:
$ X) v$ J$ V0 K7 s; v- G- I2 n0 S+ E5 b: \

7 d% ]* H; [7 _$ B* g, Q/ |/ Q' t
5 U! h$ j5 u. ]* Z' c* i: t+ [% O% W% U1 b
然后回到十六进制模拟器:, U4 s5 C- V, ^1 j2 g
转到$FF7C对应的ROM地址:: [8 a" I1 c, S6 ~4 K# R( O  c
, i% c5 i' m( q; X# F

5 X# p6 I* |8 ~! e3 w# D9 x然后复制Hxd的切页程序,粘贴到这里:, h, E) _' F( ~/ j
" O7 j8 ?- F$ d: M3 T+ f1 m1 F0 ~: e

& M5 t* k7 ~, t' J
8 L( z/ q' c/ h* _9 n1 i3 z) y没有覆盖的用EA覆盖:
, G+ F+ X( S/ T5 q+ b5 v, A  U1 l3 Q2 j4 S4 l; h
打开调试器可以看到变化:/ j0 ?. _& Z, ?3 w5 G7 b

5 @  Z3 A2 x7 W1 |然后添加$FF7C执行断点:
; }3 J! Q0 r0 l3 F/ c* h3 S+ a
4 G& E+ F2 N3 w7 f  I- R. i) p5 W/ c1 G
# [7 J$ j0 u7 y0 @0 f' b5 m
单击运行:1 f) J; i" _& P/ y
然后程序在$FF7C这里停下来了。
! g0 h; ], P4 D+ p
& @  J* [# z+ F7 d2 A然后单击单步进入慢慢跟踪,直到跳转到$8000:3 ~# e$ {0 X8 a6 X. k$ u
/ s& u4 d, D4 G% U
然后打开6502_Simulator:
, E2 a7 Y: t/ u0 Y9 s) I7 P8 [$ i* K: N# f8 U$ I8 M
再打开我写的数据搬移程序:9 O1 |" A! f) W. E! Q: O

6 _- H# ?! g) D( p6 c
( u  W; V3 \( _; y然后修改对应的数据:3 a& o" A7 U' ]  Z
程序开始地址:修改比如$8100就修改为 .ORG $8100
1 q0 V* _' \3 ]' m. D复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70! Z4 Q+ B7 Z5 [# H- Q
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82% V+ S' i' ~1 S8 _6 `, i0 d
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
0 i' B0 c0 |# x也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。* a1 v( n! A$ X; D4 d
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
+ H+ ^, H- V( R5 ]中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
3 ~& T- G; A7 S2 F' G后面的不用管。
3 ^, x! |* J- @2 ?/ E2 P( a
5 z" [6 y" u5 E& w设置完数据后单击编译:1 Y7 d, i4 W, o# Z- [

2 k1 C# K- h8 u. ]7 R然后保存编译文件:
. b5 w7 ^- `5 E
. O! |$ K" z! b选择二进制方式保存:0 O5 S. v5 M& a: j, ~8 E- h
' k6 y) O0 f; [; O

& f, h; K7 X8 _: T用Hxd打开保存的二进制文件:2 L9 O  L4 C5 h: E2 H) Q+ \

  U5 U: _' W" c4 q  E8 n- U3 N: m1 V
跳转到设置的程序开始$8100:- _, q7 q8 P* x

6 Q8 C9 o: m9 F3 p! c  m9 f$ e; T% ?
6 D- S7 x0 K' C6 S+ X
/ e* d) @: D( @8 R' T: G9 c回到FCEUX,转到NES内存的$8100对应的ROM地址:
5 p5 U$ X; B  l$ _8 I; {. D7 T, d
* p& w4 E  X6 O& x  U3 Y% A2 F& ]7 t. Q6 x& l  x' q

) R& b- V" K: P1 g8 Z6 \
0 T2 o, v0 Z, L7 P% Z) ]3 D4 P( m; x) q) J% R
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
6 |5 t2 K$ c9 l: f  x3 p
1 ]7 I- V7 {+ W! K
6 J$ [! m# x1 D# h% H9 F2 d4 L( |/ L* h- _; h/ o
然后转到NES内存的$8000对应的ROM地址:
' }$ W7 F4 a, q5 `+ g  ~2 ~5 J
: K  I5 f; @( P0 W9 _; w; G7 G% W
$ T) F3 Q) L" w& N! ~
/ x# M, C" }5 H; w  f! X
& {: L- \: @4 z7 d4 f, o; c$ R写上如下程序:
- k) J% E/ n# |2 rA9 80 8D 01 A0 20 00 81  3 N# U/ q9 ^/ T: l; L+ F1 {/ i; K3 W
LDA #$80' F7 H8 Q# p/ p3 T2 `0 ?# y
STA $A001 可写方式开启SRAM% E. L. Q, w7 ^; o$ f
JSR $8100 跳转到子程序$81007 t3 ^, `0 m+ g, f: Z+ c

2 M& S5 G; M( k9 {然后把Hxd里被覆盖的程序复制过来粘贴在后面:& \- `: o7 P. N
% }3 D% R* h' o/ Y

2 P8 w' T& t6 H0 J' A7 {; G7 f# Q3 h' o
末尾补上一个0x60:
8 m- y1 g3 o$ K" U5 C8 y3 W8 ~RTS 子程序返回" o% D, w/ s' `8 Y) M5 N1 U

. |0 p3 K( L% a/ s然后单击运行,ROM音乐响起,正常运行:
& C; ^7 m0 g6 Z7 _9 U, t
- i. d8 e- @  Q+ D  \2 f$ I+ h: P1 o% q8 c. w7 `
然后转到NES地址$7000:& N* R( s3 I/ s

! _7 h2 A% r& s( K7 y( }) s
2 h5 F7 o7 }) u
! o* u4 ^7 o+ e! _+ x$ w/ y' q; q1 U
$ m* F1 n) |' L% b0 E! z# T" i- S8 X2 C& W9 _% j9 ?& v$ }
可以看到,$7000-7FFF都被复制了一片数据。& e" ?0 `7 C* j! S0 C* v% C
测试没有问题,然后保存文件:  K6 c- q. u/ I+ o1 [5 E2 x8 j' x6 X

% n0 Y3 j  R9 }, e9 N6 H  H4 X以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
& _) j- ]  q* L$ s) [后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。$ ^5 h: `8 o# c% K/ c3 j" W
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

×

签到天数: 2060 天

[LV.Master]伴坛终老

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

使用道具 举报

签到天数: 1184 天

[LV.10]以坛为家III

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

使用道具 举报

签到天数: 1651 天

[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-8-5 03:57

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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