EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
' e' K, g, [) V
  P8 y) w$ Q4 e/ _! y8 c$ d& E+ V[FC][SRAM扩容教程(Mapper 4为例)]" r2 r9 }0 X: y3 I9 u8 C" E
( Z9 R: ~2 m6 {' e$ Y
时间:2017.4.28
" z0 N4 Q# G1 N: F3 @- c作者:FlameCyclone
. f/ Z5 }& J" E0 r* b, ^% a" ^; x工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
' [+ ^0 U5 c! T; lROM:双截龙2(J).nes
" M' U/ \4 K/ e* p; w( ?4 J* k适用:没有使用SRAM的ROM
* t0 a4 w2 k! t; w9 z
$ n; U2 i9 e1 o5 a. K  H首先用Hxd打开ROM:
4 }1 V# ]+ `& G& y! S# i$ t; P, u: V1 T1 ?: T( t+ ~2 W
然后扩容:
$ x- s  @! [; _% r9 H3 r( n  J/ M: o+ R  {; d6 G3 t# n
! x5 O: h. ~5 E

; [' D/ u& h+ V# d% U, L" P- g; _2 H, a9 {# f, `
# b4 d+ K$ J  Q+ n, ], \

; S& u8 a0 ?( O( H3 u2 J. g* \# n
3 \4 u& }, [; U  i. r, O3 D. W. I4 t2 j/ m; D# `! f
9 i& T9 l9 F3 x' }* k
先看看任天堂产品系统文件对NES文件的说明:
' u' I8 Z+ F( ^, S1 w  HNES文件格式% J; x3 ]1 `6 k7 j: g! q
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
4 ~1 ]6 @: M  ~偏移         字节数         内容 6 K9 ~$ T" G1 I
0-3         4         字符串“NES^Z”用来识别.NES文件
- P! H4 n1 J2 A: O: Z4 s' @# b4 k4         1         16kB ROM的数目
+ a3 Y+ w! t; ^4 ~  Y* r! ^5         1         8kB VROM的数目
' R' W! |6 y* b( Q6         1         D0:1=垂直镜像,0=水平镜像
! \6 D0 o+ B5 Z                    D1:1=有电池记忆,SRAM地址$6000-$7FFF / F6 q2 U3 ]7 H2 D3 ~2 f
                    D2:1=在$7000-$71FF有一个512字节的trainer
6 _; {, h( W" l- R3 H+ b  R                    D3:1=4屏幕VRAM布局
! T+ Z" ~: y' U* \! t                    D4-D7:ROM Mapper的低4位
& l; G: e3 n; M% W7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) 8 @! ]# u$ y# y7 i/ |/ c1 q
                    D4-D7:ROM Mapper的高4位 3 C' k" \0 h, F+ l$ s  G4 Y
8-F         8         保留,必须是0 ) _+ h2 y6 y5 j+ {  x5 I+ m
16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
/ Q7 q. E; R( z# \. {& b! d-EOF         8KxN         VROM段, 升序排列
- p+ k: `5 U5 d7 h4 w  f0 k$ L' g4 y* s. {6 M& v9 M9 a
然后知道这个ROM有0x08个PROM和0x10个VROM
( o* g# P) w  v: S接下来扩展PROM位0x10个:& W1 X! k3 a7 s
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
  J' V3 ?  ~; r) I9 K3 b
8 ~5 b) ^; k  j. ]由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
7 A+ o0 n5 o, d最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。
2 W0 J: J1 V" B2 w5 h: i5 A于是可以得到双截龙2的是:
+ a2 W2 ~5 ~8 Y9 v* M(8-1)x 0x4000 + 0x10 = 1C010。/ i2 P* q( [! M3 n- E
然后跳转到1C010:3 b" h2 \' s) X- M

$ \$ X8 m! W# q; L; K, S6 n1 j
5 [# A" J3 V6 R2 e. t3 L1 ~% }) D1 m. S
然后插入0x20000字节的FF:
6 E2 w" t! n  R
. Y9 d9 D' H5 I! c
& u7 G$ ~3 [& J$ j( z" L" a
1 I6 E2 f" ~* }+ e/ }( C7 i然后保存:
9 b8 K! ^8 P9 d, @
$ W: ^4 ]# c1 I! A, a% _3 \. f  A# F* {9 g$ G( J7 O- p) k  o
用FCEUX打开正常运行:# k6 B3 b$ D" j% ]! m+ x$ y
; i5 A3 G% n+ {3 y& x2 c1 _
查看文件信息:
+ e" W# A# _5 p7 }4 i+ P8 w3 y! U( F" U+ n, |1 I
- i$ ~; }! ?1 ~5 g1 }
接下来切页:, N$ d7 }6 P" ~& u! h) {; U
先打开十六进制编辑器:. n! v- P7 S  ?! }

7 {; O/ G: u9 N; B拉到滑块最后,看看重启中断
3 c$ @4 V" \! H* S7 @中断地址         中断         优先权
- ^6 _: Q5 P! g; W$ ^$FFFA         NMI         中 ( ?, B2 l: z! F! M
$FFFC         RESET         高
6 D3 a" x* a9 F. S$FFFE         IRQ/BRK         低 5 G/ j9 Y: `7 d- T4 ~3 C* p
: g5 z0 z+ n- Z! z
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。
4 L# b' M* R7 A4 S/ |1 ~由此可知双截龙2的RESET中断$FF65。
5 D& C5 X! Y, O; a接下来添加$FF65的执行断点:
4 [; h' A) _2 q7 ^! F7 z! L: @; C打开调试器:6 Y7 E$ e( m: C# s2 i& F7 r
) R, Z$ y8 x! F* [
添加$FF65的执行断点:
+ B& _1 f7 ]% k: y1 ^' e& P  l2 d6 u/ e: d, F
, l1 M3 X5 ?$ b5 W2 P) k1 }

  m) V, v' G1 e( Q2 z单击确定:
, D$ _7 J3 c( g' Y# \: X% I: x, i5 x4 p1 ~  z7 H- `7 a( W

0 ?' z6 c3 z; u4 v7 ~然后重启ROM:9 Q* y. I& P" l- r$ o+ `; `' ]
: p  r* g8 ?' t
调试器此时弹出来:
, O5 j2 y$ l4 u) y& c. @/ ]. Y6 q
然后打开Hxd,写一段mapper 4的切换bank程序:
6 g, j+ A# q8 L5 D, p# [先看看mapper 4的说明文档:
3 o5 R. m7 c+ s3 {+ q0 @3 H% W' oMapper 4* D9 h. V' a; G2 _

4 j4 N, k9 I+ R$8000:  模式号
( b$ B/ D7 s; W5 Q: S, H' K        位D0-D2:
+ N, h4 H6 O! Q& ?/ z/ b        0:选择2KB的VROM存储体映射到PPU的$0000
# [  _% l2 B, e0 j  b        1:选择2KB的VROM存储体映射到PPU的$0800
! \5 H7 D; C, E- G8 \$ E" I        2:选择1KB的VROM存储体映射到PPU的$10007 n: U! J+ Z6 D
        3:选择1KB的VROM存储体映射到PPU的$1400% H3 t5 g. k$ B- i; k- j+ N3 D/ r
        4:选择1KB的VROM存储体映射到PPU的$1800
/ U" l* q  u6 ^$ T: Z9 X& m6 ^        5:选择1KB的VROM存储体映射到PPU的$1C003 \5 Y; ^' W& Q+ W
        6:选择8KB的ROM存储体映射到$8000( d0 j5 m5 Z, m& n
        7:选择8KB的ROM存储体映射到$A000" _6 u. d+ s, Y: H
        位D6:$ @6 n7 l/ ]# ~6 ^& B' ~
        0:允许擦写$8000和$A000" ?* x3 Y5 ^: p& I6 z8 j: f
        1:允许擦写$A000和$C000
/ ?; t' k/ C- v5 A; K9 `        位D7:
0 W1 u' z" X& k4 r( ^        0:模式号D0-D2使用普通地址3 n3 o6 n: G% b% q( J
        1:模式号D0-D2地址异或$1000
. d7 l0 P1 J* G1 ?/ W  z- d) E0 ~9 G% T. C- x; C
$8001:  模式页面号9 I6 s! p/ k/ m; N. I* G2 W$ `" z& \* L
        写入一个数(00-07),切换存储体到对应地址" A0 }. B; {8 j% Y/ H9 X

; Y' i. F! J* q2 m$A000:  镜像选择- b# W  H( r% q' E' w; m3 E% g
        0:垂直镜像" F/ \, X( E' _9 Z" B, @) ^3 Q
        1:水平镜像
6 e. r, o6 A/ M( Q5 `" b2 N, f9 R5 P
$A001:  SaveRAM 切换
$ V2 Z; E6 y1 x        0:禁用$6000-$7FFF
: S- E+ F. x( ^% x2 |# A, `        1:启用$6000-$7FFF, A- A0 r1 K" b7 l0 M+ X6 x
: T. a  k: X. N. N- E
$C000:  IRQ计数器
- @/ S# S  [1 i. K# b; M        IRQ计数器的值存储在此处
* t: i8 N" ]& v; D7 q& C1 Q3 R1 t5 M: W  W
$C001:  IRQ暂存器
  G3 O3 l; t& Y& G        IRQ暂存器的值存储在此处* X: r5 s1 |5 P; a- f

. \* K1 ]6 H4 m: [- _$E000:  IRQ控制计数器0
6 [! w7 s' _# ^8 M, g        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ
( K4 M9 W! P' f- e; u% i8 \3 a9 L9 z1 T5 W( U
$E001:  IRQ控制计数器1
7 ?) k" X8 }* z0 A1 U  c6 h% J        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
* `5 `. F5 C( h* ?
  o; @3 r) {& I: X; o那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:; H$ j% q$ [( u0 }
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 689 K6 K# Q( ^7 @. j+ \, k" U
PHA      累加器A入栈7 q" e2 m" R  K
LDA #$06    设置切bank地址为$8000-$9FFF: Z7 _% ^. h4 W/ {: F4 Q# K/ O
STA $8000  H! y. u* N) ]  w' L
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
9 a# h6 @( z7 {/ g2 d3 h. q; xSTA $8001/ C8 P/ O' \$ B! o2 ?! M. h# H
JSR $8000    跳转到子程序$80002 a3 z: H7 ]! J$ d" M2 [
PLA     累加器A出栈* V+ O3 n' K& b4 v3 }5 p/ W
" M2 X7 o7 q1 x2 q
为何切换的bank号是0x0E呢?
2 h  [, C9 j* N( x2 a6 j/ m# z因为在扩容ROM后,文件PROM结构为:
$ o+ N, ^- M. S原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM$ @3 ^; j5 T' i# \. |2 A  k% ^( m

' ~$ e( E( I6 N4 U; pMapper 4切bank一次是切8KB,那么文件结构就是:% Q5 S4 X- x4 [3 G' w8 d, a
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)% }0 h% a! S, X  n' A. Q
因此选择扩容的第一个空白bank就是0x0E号bank。3 S+ A& d# q" H, ]; e# Z! P( k

% u( d- e, X2 R7 c) u
) g: G6 W; }" d4 {% C, j然后去调试器找RESET中断中可以放下切页程序的地方:; G2 y; h! Q8 s+ u, t* ]
首先长度要小于等于自己写的切页程序。
! d- C( v# h$ S可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
& ]& c5 p+ K! G1 g+ B4 }! r; z0 u" w7 X+ p
然后跳转到$FF7C:9 h- O7 u$ e! S1 k  P1 J
/ F, }9 p3 ^$ e0 M
3 n; i# y* f- c7 @

5 T( _7 v; [) v, n. b" ^( W
1 }: |7 H, }$ a4 n; N复制下可以被替换用于写切页的程序:( @, i% V  Y* b
先选中,再复制:) `! a6 R$ C* ~

$ O: p, `5 `1 a
  \$ {* Q  q* C0 U: Q6 h在Hxd中新建一个文件,把复制的数据粘贴上去:+ S. C  }" t2 L+ o) U6 ~
. C# E5 ^. j' m9 u, Q( f: h) ]
& k/ m4 B# @4 Y/ p: p) K$ g% s

# X; E  @/ ]* \* C1 r% `
: G  L8 O! @  J2 n  M然后回到十六进制模拟器:
6 F  i7 @( O( ?5 v转到$FF7C对应的ROM地址:
/ `; Z6 o7 {6 I. j9 v, @2 d5 G  B  h" l5 U  a- d: n
0 C) u4 Z5 i+ W/ e8 Z  ^
然后复制Hxd的切页程序,粘贴到这里:5 o5 J( [2 f; E5 |2 |0 y; ~

8 o" O6 m( \4 O# ]" D5 o
7 d  {2 F; n8 M, z' e% Q3 M7 e" G5 N, k9 a1 Q9 J! t% ?
没有覆盖的用EA覆盖:( \! ]- {2 o* t% P

4 M5 P4 }. I' }3 p' ?9 ^5 H2 v) H6 P打开调试器可以看到变化:  ]3 W8 b; T( M2 ~# \, E, m
/ D) G& o- C" M$ G3 j
然后添加$FF7C执行断点:
: G9 q" g1 O" |* u$ M+ w- C4 ?- K% w) p9 e& `$ o2 [! J6 h6 M

8 r2 Z' m$ a2 X+ T8 E- V2 `$ S' a+ |  ~& g4 \
单击运行:3 q- @7 G! o0 V7 ^+ q" Z) D
然后程序在$FF7C这里停下来了。
3 P0 Y$ J6 K" x* ^1 f& Y9 d2 x, @6 g0 G+ F" o
然后单击单步进入慢慢跟踪,直到跳转到$8000:/ l- V+ B  ~6 N4 s/ {! P6 |+ X
4 D9 A3 t& O' Q- Q4 M( |1 {
然后打开6502_Simulator:
& ~2 j! s3 ]+ i% g! ]0 r+ h* _( L; M' Y* R
再打开我写的数据搬移程序:
  r' i2 h& J  O; z2 C1 M: L/ T
7 i: z: \7 g) m2 R1 T# B( I
2 f- L- G8 G; v4 u  }然后修改对应的数据:' V% p2 Y$ X0 D" d# E
程序开始地址:修改比如$8100就修改为 .ORG $8100
3 J6 d& q' X+ {/ \$ w9 p" O& q& Q复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
. i/ W* c2 ~# w0 U* M从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82: h5 S9 r, e: ?  N0 E
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
( L8 ^$ ~5 ^# }/ I' E: `3 \4 A. h+ J, l也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。9 k  }# g5 d) t# L! [
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。% G0 k* _# M2 Y
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。- ^7 }3 m" Z" Y+ w2 r
后面的不用管。3 T- @; o& i  `7 H
, O! G' E) M! X
设置完数据后单击编译:4 h8 n- r; w/ e3 W* h. x  e3 d+ }
; X! [! o5 z: |' }% `% G
然后保存编译文件:2 _- O! G5 P$ T) u  }8 G

! B2 r. D- ?% n; ]. c选择二进制方式保存:
, t' S. o3 g& \/ j% O3 {
2 \+ A( Y* g& H4 ]- F& P' `# m
% `: W! X; w3 R用Hxd打开保存的二进制文件:: O) E9 ~  I0 p6 R
3 F, S- s* C! k$ t7 E! q' b/ R( t

/ o" \- Y  J- J跳转到设置的程序开始$8100:7 O$ d# W1 l. S% H/ c3 l

; \0 E+ m+ a; g# p" ?/ v; U, H" P9 U  C6 G: g% u  k4 J

& d) b; E7 s1 @回到FCEUX,转到NES内存的$8100对应的ROM地址:
: a) Z, e  e2 j% `/ F! V- x( P) ?* O; Q

! H" E3 q1 i# l! l. P6 t) Z% W0 t$ ?) |. t- g# e! Y
4 P5 w2 O3 S% @4 B1 }

5 I  k) d: V8 M3 a8 ]% ~* t) B然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
$ _' z4 ]! J& o+ l# _9 n
1 ^1 `' `* J* o$ ?& v4 r. t  Z5 l9 G- \

+ u0 W  y4 C5 d% M, V" Z) A然后转到NES内存的$8000对应的ROM地址:: p+ a5 P1 }) c
- g' S  k  _* d) H( g; h- c

8 ^( ]4 |+ R& @/ F9 B: g) ~3 [  \# X) q0 i
; v# P* f+ Q) M9 J9 }1 j1 [3 A
写上如下程序:
9 }! y2 y$ l' YA9 80 8D 01 A0 20 00 81  2 o' a- I- J4 B9 Y! z# P( `
LDA #$806 y6 a, }/ |! x0 S; ]4 f
STA $A001 可写方式开启SRAM
, B: ^2 H9 f# x) O1 E  Q6 G, `+ }JSR $8100 跳转到子程序$8100" q5 d2 p, g1 _* i
; @  Q# P9 `1 {" C  ?) u# ^
然后把Hxd里被覆盖的程序复制过来粘贴在后面:: t* F# s2 L! }& Y2 j- p$ F

- v1 S$ A' P" T: T: N% ^' t. m! b' a& O& R' v0 e3 H% u
, N+ r, B+ e/ Q% C3 t
末尾补上一个0x60:
& N: _$ _% T! G, T. `: tRTS 子程序返回: Y2 C* s( [' C; V* k% d  c/ ]
7 f3 q" b* H% i6 }  `9 y: x
然后单击运行,ROM音乐响起,正常运行:+ `8 J+ ^! L, ~9 }& M1 Q1 J4 f
0 k7 @# Q0 g% [0 D! {
/ Z' c( ~8 D* J1 g7 M4 |
然后转到NES地址$7000:& d9 ^8 o3 h' _+ x  ?3 @0 W  m

. f& o3 d: j! k! D2 o8 f' G8 D/ q: H6 d6 I2 w$ O, |3 [. d

6 m) i  g" @- J
- u8 d4 i+ _* r% n* d; ?7 N: M3 b2 \- G
可以看到,$7000-7FFF都被复制了一片数据。
5 S4 \" z& G  N- J测试没有问题,然后保存文件:8 F+ e2 ?  G& l& Y  n: d- D* _: {

. Y$ U$ Q2 D2 n. v0 Q1 K以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。' l6 r$ @% o, ]! ]
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
( ^8 N8 z% |- |8 ~
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1183 天

[LV.10]以坛为家III

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

签到天数: 1594 天

[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-6-12 16:30 , Processed in 1.081054 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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