EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
- P2 v. C/ W- [) x6 B% F2 m1 Y. Q1 G
[FC][SRAM扩容教程(Mapper 4为例)]1 V  I! L/ l4 G: C* T" a6 C

: v- n2 V6 S; b0 F* f9 M时间:2017.4.28
  Q+ S; y- u6 R$ ]作者:FlameCyclone
2 A! v2 [& t& x: Y6 n* `& v7 k工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator2 Y2 ^2 F3 g, Z+ q& Y0 Q: Y! o
ROM:双截龙2(J).nes
0 Z$ d/ d& \1 z% Q, H" k. L( C# ~适用:没有使用SRAM的ROM
8 P; d" G. i+ ^5 f6 _! d- h: n4 o* o3 S( K1 P# j; S
首先用Hxd打开ROM:& N- ]$ o, n7 L9 _' H9 D, C& G# S

- ~# h5 P( R; w( W/ l' u+ N然后扩容:8 Q% A8 x& F$ O; v, F
0 c* V; h5 I* |8 E6 @+ O
" L1 T, f. v, B' S9 r7 V
* o5 o; z6 H5 d: t/ J  ~7 _
  G  Q0 H. n. Y: ~3 I3 ?

2 D- V8 ^* m2 M! f! E
! s% j8 P/ T) z! i
% Y0 H8 I* J# w# d3 f
" p1 P6 Y* b# ]. p0 V4 z6 _4 g8 {! ?" v+ }# K
先看看任天堂产品系统文件对NES文件的说明:
1 X: ^  O: S# c: s* V5 \NES文件格式
/ L2 b* n! ~- s& Y.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 0 g2 g5 K: r/ t! }, V& w7 c5 D! ?
偏移         字节数         内容
) a7 u5 T/ |' |: t# {6 k4 E0-3         4         字符串“NES^Z”用来识别.NES文件 " k8 c1 o" X3 y
4         1         16kB ROM的数目
4 h9 S, J9 o( Y1 Z( m/ Q5         1         8kB VROM的数目
, [1 g  j" x5 U- R* f8 V6         1         D0:1=垂直镜像,0=水平镜像 3 F: E1 w. Q1 G: S3 S! g$ h+ V
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF & k. ?4 J% n9 \% @
                    D2:1=在$7000-$71FF有一个512字节的trainer # J8 m! }& H+ M
                    D3:1=4屏幕VRAM布局 / p2 r& u3 w3 Z  _8 I! C3 g/ b, V
                    D4-D7:ROM Mapper的低4位 # o" z8 C9 J$ x5 p, e# G! H
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
7 u4 q- {4 r; f+ z' G: q  j1 d- V3 |                    D4-D7:ROM Mapper的高4位 8 T) f; W( e( x- c
8-F         8         保留,必须是0
0 u2 }, j3 z; c16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
; `6 Z7 t0 t; b. L* F5 u; b$ {-EOF         8KxN         VROM段, 升序排列
# k6 t) ~  S& j3 Y2 p5 k  U! ~
然后知道这个ROM有0x08个PROM和0x10个VROM" C6 w. ?1 ^* W) v: i; X' X- n
接下来扩展PROM位0x10个:( Q8 P6 {! H9 ]# K2 V
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):1 o9 z: h6 V) i- b! K, {/ ?

, d: G. Z8 `- }7 J, ?: O由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:+ E0 K) V8 u: C% N6 R
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。* o2 N, u& S' ^1 O- r4 }# J
于是可以得到双截龙2的是:
& c) r' Q2 m1 v& |+ X(8-1)x 0x4000 + 0x10 = 1C010。
+ h; A" \' B+ V! R* k; ?2 m然后跳转到1C010:
! `8 W( o/ ]5 q% o6 Z* s+ |' h: I( x5 [' W+ s4 {
& Z+ o2 [1 j- n# |* ^

( u) }& P: ?) f- w; V+ w5 b然后插入0x20000字节的FF:2 I3 a% b3 L3 S* C

& w. V; q9 c! m. ]) a; N: ~' E; x% S  Q/ \) U; u: b. g# v, k

' b3 h: W1 V7 K然后保存:
7 L3 `7 e( i" x, H2 d( k% r) h0 d" B4 O& X& c0 ]9 e. |

& D6 k/ v" `4 v% r# \" @用FCEUX打开正常运行:  z. T2 e: I$ S
* P( P( W$ g' B' i
查看文件信息:
. s" E; V0 {3 V9 g; K0 t" D: L% s9 @% G
' {9 S' x/ D& w" N! _: B  Y2 t' [5 u+ l; K) r) }
接下来切页:* t* `' C9 Z) C
先打开十六进制编辑器:
- m! I2 S3 }6 u( X" Y2 j6 m6 R) T
) l' J/ q6 i/ P; P拉到滑块最后,看看重启中断
% D2 P- ]. m6 Q- x* A" _中断地址         中断         优先权
' B9 I( \# @  x/ h( o# ^' @: [$FFFA         NMI         中
1 k9 a* R& c, U7 M3 V) G$FFFC         RESET         高
: l6 p9 ]2 S7 R0 ?$FFFE         IRQ/BRK         低
9 ?6 |6 ~/ @. c6 a+ B9 U9 d6 i8 Y, G8 p7 h; @: @
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。; h1 l, z1 P0 I1 e# J4 x' P$ g! x
由此可知双截龙2的RESET中断$FF65。: Y, j$ U- }0 t. c9 t, h7 }8 Q
接下来添加$FF65的执行断点:8 n% Y9 g3 `8 n5 B+ f' v# U% F
打开调试器:4 y4 T/ q- e% |# P- g5 I
% g% U0 \% r8 G6 ~4 v  i
添加$FF65的执行断点:
; [+ L/ ]1 ]* n
( o& W5 A5 v. r  w; T$ B) |/ ~  O: |9 ?& e

8 [7 k0 q3 Y" j/ @+ L( N单击确定:
. p' o% r" O1 m/ `; a) x; i
' r' Y0 T; d9 s5 c; S0 b0 S: s. n/ P* ]2 {) w) a
然后重启ROM:- K6 Y9 a( E' N* k& F: w
3 n2 B& z  P6 ^: U  H( _# Q& a! @
调试器此时弹出来:
' e- Q5 }6 o! H5 F& g1 ^" I, [. j, r- k4 x; M* |
然后打开Hxd,写一段mapper 4的切换bank程序:# c" w# O; c: ]0 }
先看看mapper 4的说明文档:
2 |. c' O# Q* g# fMapper 4, _1 T! B$ z1 g6 A
; H3 N5 A1 v# [! i$ V! |
$8000:  模式号
6 t# U( N- S* C) Y9 q        位D0-D2:
0 G+ [% k! D3 d' L        0:选择2KB的VROM存储体映射到PPU的$0000
+ J. T! I3 a* D2 n        1:选择2KB的VROM存储体映射到PPU的$0800
9 W( k: }/ }2 X+ f        2:选择1KB的VROM存储体映射到PPU的$10001 `* O1 ]) Y* r% B8 _& N
        3:选择1KB的VROM存储体映射到PPU的$1400
- ^5 d  F5 u* ?. B+ T. X        4:选择1KB的VROM存储体映射到PPU的$1800$ m) o$ s4 t3 N
        5:选择1KB的VROM存储体映射到PPU的$1C00% |) X8 {( i; O9 R8 N
        6:选择8KB的ROM存储体映射到$8000
' o" h/ n8 g4 a8 U        7:选择8KB的ROM存储体映射到$A000
0 G) N5 M6 w/ \: f* |# |0 N        位D6:
) j+ G  l# F% K! b' D9 t        0:允许擦写$8000和$A0007 |4 ~- }# `' p& F% Q5 t
        1:允许擦写$A000和$C000' p( d/ `6 A9 u0 f( ~  L3 v$ c3 w
        位D7:+ j* I1 W- s* Z7 J6 \! T
        0:模式号D0-D2使用普通地址
- c3 K8 }1 o- u% r        1:模式号D0-D2地址异或$10006 S+ W' z0 j, P* }/ n
, S  ~% U! X1 [6 J: {
$8001:  模式页面号' Y4 Y7 I3 N0 l3 ^: I$ l, `
        写入一个数(00-07),切换存储体到对应地址" L6 w0 w8 W5 |& c9 B
- l+ c5 \( e6 ?9 m4 m
$A000:  镜像选择9 [: f5 B4 P6 \% F6 C- V
        0:垂直镜像
% E* f. `- q' a( Z1 {        1:水平镜像  p5 c: k2 T# R! P6 i

1 ?/ D5 x% x2 O) t4 K9 x$A001:  SaveRAM 切换* q. L1 V# D% t4 g* y2 p1 |$ ?
        0:禁用$6000-$7FFF* g1 z! Z8 F' l1 R1 E8 T, p6 f
        1:启用$6000-$7FFF! R1 X6 N7 K# V5 Z
. R& W  H$ |6 Q4 }- E3 r2 |( J
$C000:  IRQ计数器
6 O, D- n  r/ u& z5 n        IRQ计数器的值存储在此处
/ r* L- `& D0 R' V! \) l0 b" A
3 O" h1 s# n) Y% S  p; ?$C001:  IRQ暂存器0 {# u% ]1 A- `! p9 o+ v
        IRQ暂存器的值存储在此处
. H. a& y0 u: Q; p$ x- c
1 ]; _  @5 U6 J9 D$ [- Z$E000:  IRQ控制计数器0
. E- v+ D" {) j' q# n) H        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ; b* i+ |& Q5 d" ^

0 @+ Q) u. c. `* ]; ~5 A$E001:  IRQ控制计数器1
6 \* l, y  j* ]3 L- _+ l- r3 Y* h        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
1 ~. L: z5 G0 A6 m" \4 R  [/ a$ Y* L6 d2 V/ D1 v1 h
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
3 L; i/ s! a2 h4 g2 J7 D; p" z48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 689 ?' e$ S' ^$ g6 D' |
PHA      累加器A入栈
9 T6 o) J& y5 S1 d8 U6 y! {1 MLDA #$06    设置切bank地址为$8000-$9FFF. P, D3 {: ]( L( u8 L
STA $8000- s- Q# n9 c0 r$ j8 z9 l8 K
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
5 @0 j3 ^! r8 ^7 d4 q$ ASTA $8001& B0 G( X  b% n) b$ D
JSR $8000    跳转到子程序$8000
& ^+ v! L& X- MPLA     累加器A出栈
/ @% P' E5 o, e9 m( ~. X8 f9 x7 r0 \9 u. _+ b+ s8 Q8 G! T
为何切换的bank号是0x0E呢?
! [& g5 N- c0 x& p因为在扩容ROM后,文件PROM结构为:
9 W5 T( [4 e+ d& m, q# N原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM% s' v5 {/ J1 H% J- x8 [
" B, u5 `& k' w8 Y* N! Q
Mapper 4切bank一次是切8KB,那么文件结构就是:
: q7 L3 V4 \/ e! m原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)3 \% m- D" V6 @% \; B$ Y" a
因此选择扩容的第一个空白bank就是0x0E号bank。
5 j: N" @* m! |, C, t. z0 [9 w2 a+ e, B

  O+ ?- v9 c4 Z  Y然后去调试器找RESET中断中可以放下切页程序的地方:8 }: @" \" s% {/ y, C4 H& _$ r
首先长度要小于等于自己写的切页程序。0 e$ U5 T! M" r3 W# x1 {4 s. K
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方): w# s. D0 [6 T' k2 @& g& Z* b
2 F# J( ]& ]  N
然后跳转到$FF7C:
+ c2 B1 a' x' k
3 s( G1 T* N. W2 k: R' x& t- ]# M, b+ D. J

. i7 B6 @2 p' F3 q6 |( \
9 p5 ~' M' H0 H# x( F, V复制下可以被替换用于写切页的程序:
. Z, @; w0 ^6 V- K+ I先选中,再复制:
, ?1 Y0 p: a4 X2 d. g  g$ }9 y. H
6 h1 o) x# ~3 `& {) R. L# E7 Y1 c) @2 g9 o6 y$ Z+ W
在Hxd中新建一个文件,把复制的数据粘贴上去:
8 a+ k! T& c& q) t( K. [" D8 v9 d

5 k; K. I5 \% a% ]! ^# d
9 F) Q  n% T% z. Y; M! x
, ^. D- d/ L9 d, f' d! d7 f然后回到十六进制模拟器:
3 N2 b) X, |7 M$ @' n转到$FF7C对应的ROM地址:
# [* x) C, ]( k& r! o/ ]3 |( W  R/ k& b' T
. I( Z2 f$ T! b  W7 d" X' C
然后复制Hxd的切页程序,粘贴到这里:
4 \1 S0 _6 V. t* u8 U6 Q$ R$ h& u. z2 d, _
3 c% c- D9 n; B  l% Y  K
+ u& g4 w9 x7 |8 U* U7 m, ?0 y+ G
没有覆盖的用EA覆盖:
8 Q8 \$ Q3 P, p' t$ g$ J5 r2 @; l; `  x/ `
打开调试器可以看到变化:' a7 z. K7 }+ d. u

8 P$ g9 M7 M1 ?! `% ]5 ?/ y3 @- K7 g然后添加$FF7C执行断点:6 n4 b% t) `& s1 t. F/ Y' i

/ m% K0 m) B  _+ ?- c8 ]8 K& C% j+ v8 ^/ {3 }% c0 W1 g, S  I! [
& e8 I1 ], u" Z% `% K! ?
单击运行:
5 _; l3 @; y4 P' u然后程序在$FF7C这里停下来了。
3 t9 ?$ E# }4 u4 \4 V; E
1 _/ |' Q+ \6 A5 q( `然后单击单步进入慢慢跟踪,直到跳转到$8000:
5 z& z* N$ b2 P  P3 |. k
2 r: ]' r$ ]) R+ R! ^然后打开6502_Simulator:
% w  [( m7 ^  x) ?
2 J" Y( I$ y' k/ j6 T再打开我写的数据搬移程序:
. X+ f+ h3 M0 y( b7 K
$ g8 X7 y" m$ F2 ]4 ?. z; @
2 G9 X' Y0 i/ k  h$ a5 g6 t+ ?然后修改对应的数据:* O( o; i# Y+ B
程序开始地址:修改比如$8100就修改为 .ORG $81005 a* r/ \# ?. H
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
1 G& q, `" `8 H+ x4 X从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82& r  K, t+ Q$ Z$ `2 |: g( L9 b2 M
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$911 c6 B$ B" ^; {$ U/ P4 B; [* o8 X- P
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。& I0 V- A$ l) K: c+ r" e0 h2 i! m
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
2 t+ x  T# }! w8 z% `$ E中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。* A* ]" r$ z" n, W# l2 W1 ]
后面的不用管。; U6 E4 r/ I# E" |' f7 d6 F
: V0 G8 A7 B  j: s
设置完数据后单击编译:
( N; ?/ I: d# D/ u( O# K; k! F/ Z
然后保存编译文件:  J1 v, X- K6 d8 y" [
/ x# {) j( E# @
选择二进制方式保存:
/ `  k/ w: Z! A, b
0 r# C" I+ e" e- Z# ^1 ]
4 f# ~  z, @$ y' s5 V$ v! Y1 p8 H用Hxd打开保存的二进制文件:# r5 U. E0 k6 k1 I

6 z& e4 O4 K, ^1 h! |
' h' H, N1 S. j) ^4 [& b' _; f跳转到设置的程序开始$8100:5 K* ^/ a/ v- \

. k) u7 C6 x% f! v: E% l7 K
4 w  K; ^3 P7 X/ A" a  Q4 i
3 r. A7 O; L  J/ Q( s; @回到FCEUX,转到NES内存的$8100对应的ROM地址:
" E. [9 {, _: H, F3 q
4 C  f) q! P& A: Q" h" L/ V  T) f5 Q4 a! G/ H% b/ _

6 u3 B. F8 E& B# a3 c6 W& a" k- o: B* E% L7 ~
# a2 d* O+ c1 [$ g5 y( K' F
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
. V; }+ p. n, B% j
. V! |3 t# l$ u, @( X
' E& E( a$ c( I. o; G) j
. l7 T! }) U; g. L+ m然后转到NES内存的$8000对应的ROM地址:; {( {0 z* a. g+ @9 l/ n

) Q9 S: d; ]" E$ \  }
& a" c) i: V4 y! t( q
' J4 h* I4 T- `5 o6 Y
1 Q$ J0 j' k' f写上如下程序:
( A1 M1 c2 z5 \/ S, IA9 80 8D 01 A0 20 00 81  ! P$ t( p% I( o+ F* l+ K! h- b7 _1 L6 Y
LDA #$80
, Q& E7 }7 S+ T! d; m  D7 SSTA $A001 可写方式开启SRAM8 U, S5 y4 L% `: `  |
JSR $8100 跳转到子程序$8100
  r. B+ c; z- `9 H1 ]6 W+ X% a5 t& o$ K2 T1 u9 S
然后把Hxd里被覆盖的程序复制过来粘贴在后面:
$ O7 |& A( t: Y. Q* a4 o' j, N# j, ^& Z6 p( H2 Z7 t. s7 E
& ?6 P2 \3 p2 d: S  ]: u+ `- I* {
/ Z0 D1 i! T' }  B
末尾补上一个0x60:( j. t0 O" S2 Z+ `
RTS 子程序返回% e3 s; h8 _  X- F9 b

' {: S& l# ?, _然后单击运行,ROM音乐响起,正常运行:
5 V5 Q. X) h3 u4 W* C) h
6 m8 `8 k/ w% }+ c* N
% N4 A5 W/ b/ a* h然后转到NES地址$7000:6 R' f  L5 ?; C. J7 k( h

" a6 Z: o# r( ~0 t6 {2 q; D2 c+ R1 W2 c# y: @3 A3 v. V$ j, @# f

; G6 N6 u$ l+ s
6 `7 |/ o' q1 w6 k4 N9 f( o! i+ g; S9 h# h3 V
可以看到,$7000-7FFF都被复制了一片数据。
4 q1 e5 w, _6 V. D测试没有问题,然后保存文件:6 J! h; j+ A% a' n! y9 F( _8 E+ o
9 x3 z% d! W$ u
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
+ \" z" O# L4 S# R1 _$ j后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
# p* l' c$ f2 E6 @* O5 D7 H0 z
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1183 天

[LV.10]以坛为家III

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

签到天数: 1525 天

[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-4-3 16:57 , Processed in 1.274414 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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