EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 ) s: c' a5 t1 h, j+ O6 x2 J# ^' f! E
" h5 y* r* @* x; J# Q; n2 l
[FC][SRAM扩容教程(Mapper 4为例)]
; s% O/ I; f: E0 c/ H$ S8 A) n' t% w) m% {. i7 |
时间:2017.4.289 z, |* m) X* F0 a; @
作者:FlameCyclone$ P4 Q) A% Q2 ~. t% {
工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
6 `; n6 o1 \7 g2 u9 ^1 \+ ^ROM:双截龙2(J).nes$ w! p# l' u1 J- H! y( m
适用:没有使用SRAM的ROM
7 [, X2 {5 P% i6 m. Y9 t7 ?/ F  u0 b: {$ G0 T. x
首先用Hxd打开ROM:. B3 A* F4 [4 S& U: N. k6 _
' ?! @: r' D: K: s+ R7 g- u, ?
然后扩容:; a7 C( r2 f0 J# e5 W1 K

6 n, z' ?. \5 f& ^
& t6 x( ^4 r& z& H8 \1 ~1 A$ [; T7 O

* m! a! v7 _# P: a- {( z1 ]
* j& S5 J: V% b7 v, B* H* p- K1 W

$ R! I* n1 `. Q  T& D" w
7 N# X/ C4 W" H2 @, L/ y
% h! W+ j3 Y$ |( u9 d先看看任天堂产品系统文件对NES文件的说明:
- G/ J+ W% ~/ gNES文件格式
- f% C' Q' ^9 P; O% z6 T7 e& G$ ]7 @) y.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
. |% X$ y+ J5 E3 x9 R偏移         字节数         内容
: j' j, {/ V4 c0-3         4         字符串“NES^Z”用来识别.NES文件
2 d% i' m- {- }+ @2 @4         1         16kB ROM的数目
7 c9 d+ W" ]7 O5 c5         1         8kB VROM的数目 ( p6 i" C0 E% \. L0 d9 G
6         1         D0:1=垂直镜像,0=水平镜像
0 C2 u( M1 ~1 o( M0 o1 R                    D1:1=有电池记忆,SRAM地址$6000-$7FFF $ {) [' W& H0 d. B/ A
                    D2:1=在$7000-$71FF有一个512字节的trainer 6 G& @& j% ~: g# a" b( h
                    D3:1=4屏幕VRAM布局
9 E1 J- P" q. `& }( o5 h                    D4-D7:ROM Mapper的低4位 5 c" {8 Q$ E+ t: N( v+ t
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) 3 S+ ?) W3 p% @( g/ q
                    D4-D7:ROM Mapper的高4位
+ T1 i0 R8 G# j1 t8-F         8         保留,必须是0
2 e" _4 t  B/ L2 q16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
+ A7 t! B! E1 g( j; J+ I1 R( e& U-EOF         8KxN         VROM段, 升序排列 1 R0 \0 f+ c# Q( ]0 {! s

! B  z& D" h" M2 r& f4 q然后知道这个ROM有0x08个PROM和0x10个VROM6 A' k, k; p. r  z& W
接下来扩展PROM位0x10个:
$ {  S" g6 f( N4 K( t先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
2 K# {5 `2 s% f9 A( E
  N2 k* J# U" p) k由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:6 n6 j& G! H7 B, \* x6 M! a
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。
* g- U0 c! J$ ]% }) h3 u' n7 e于是可以得到双截龙2的是:
( L3 `2 I1 _5 \  m9 c7 W' }(8-1)x 0x4000 + 0x10 = 1C010。3 p! m5 M6 t, s
然后跳转到1C010:& H6 I# Q1 l- }& Q' x: D
+ l4 X; n  A; T: p1 h2 a- B
6 O3 O0 v5 n6 w' U( p: S
: Q! S; }* T$ z% u! T
然后插入0x20000字节的FF:. u" ~, b! A8 e/ V; ]

9 t! @* I! a+ r# j3 `* e/ @$ N) F" h; h& f* d5 O0 Q
2 @  Q& Z  M2 L; s
然后保存:
7 e) _) O: r  p, S2 m. V- N( q* ?% T* l- A

' Z% s/ G0 g' m9 o) E. W+ M用FCEUX打开正常运行:
0 k" S8 t2 d- D
! l( N% m* t. T- p# W查看文件信息:
" g- i4 Y4 b) w0 s. v5 p, g: y& t+ @8 n) i7 L$ x
- E  d# N) @+ h: {3 z: H
接下来切页:2 N& T* _' t  }2 G9 Z
先打开十六进制编辑器:
6 ]& b* O1 Z; G$ w- {" G( ?; H( {3 ?- y" W$ d' Z) }2 C* U
拉到滑块最后,看看重启中断) ^6 }7 ~  @( C6 c
中断地址         中断         优先权 ' v7 U$ K. U' ?8 L! w+ X
$FFFA         NMI         中
! m+ o" E  M# c5 X. x4 o$FFFC         RESET         高 + b8 t1 V; X) `8 S& g
$FFFE         IRQ/BRK         低 2 ?( S, Q( H8 ?8 I
6 g7 x! j; B6 F1 `% r0 e/ E
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。
. j1 ?6 s8 t- x, A9 X+ @! n. K由此可知双截龙2的RESET中断$FF65。
( k# W! i7 a" ]$ ?2 R- e4 r* x接下来添加$FF65的执行断点:7 H2 j/ I% \9 z7 I
打开调试器:/ k- D# h1 q/ G' }

" R8 @: I3 h; T* W: j; y& O( ?添加$FF65的执行断点:: O6 x! z4 \" j  L
% e& h% e6 L7 X3 c1 W- o
% L. r# [: O. U. |! U; V: i
/ Y" H+ I! ]" Q4 u6 N1 W
单击确定:
8 F7 y9 h6 B- D' k* o3 m) {) V5 _- g$ V: V* G
- Z8 a* [# y1 z) [; O! R) i% f7 @
然后重启ROM:# E2 y. o0 E* C. C+ T: e+ E) v

- E2 ?- e: W- }0 F0 |调试器此时弹出来:
; y6 w8 ^3 y: [* a" o2 H
; X3 J) A8 h! a+ p/ P; Y4 V9 [然后打开Hxd,写一段mapper 4的切换bank程序:
3 Y4 [% W' x. c, f' c  h+ `先看看mapper 4的说明文档:# Y( @$ A# R1 D! x% J3 I
Mapper 4
) E' e, D4 [4 r6 j3 ?: K; V( g: J; n. ^; {' l, O7 R& c9 v& c( q( h
$8000:  模式号
2 r: G- o7 t. h7 t        位D0-D2:
6 U/ o1 P2 Y; K, \1 J& i. Q4 q        0:选择2KB的VROM存储体映射到PPU的$0000
  X5 \0 M  Y- Q        1:选择2KB的VROM存储体映射到PPU的$0800
1 Y3 o" c0 b( b* {$ [  u$ X* W        2:选择1KB的VROM存储体映射到PPU的$1000
8 E/ M& P% r% B* p1 X' Z  n5 S        3:选择1KB的VROM存储体映射到PPU的$14003 J! C; v+ y* O1 ], L
        4:选择1KB的VROM存储体映射到PPU的$1800
, P3 {, Y" Z3 b: x        5:选择1KB的VROM存储体映射到PPU的$1C00
8 E1 ^) q( Y, {$ Q5 x        6:选择8KB的ROM存储体映射到$8000
' o' E$ t5 p( R6 A        7:选择8KB的ROM存储体映射到$A000: S8 ?" `- g( q- C; Q4 w
        位D6:
' T2 L8 x8 i+ }$ S. A3 v        0:允许擦写$8000和$A000: _- [! k) T# Z2 [& O% c
        1:允许擦写$A000和$C000
/ K5 m( w, Q5 D2 S        位D7:% S' r7 O4 T% {7 g
        0:模式号D0-D2使用普通地址
; i: t8 G) q2 i2 R. N, e        1:模式号D0-D2地址异或$1000
/ B5 |( ?, C( `& J
7 R& e; k9 k4 @  k& p$8001:  模式页面号, I7 R8 f( P/ A4 d; }0 i( }1 p
        写入一个数(00-07),切换存储体到对应地址2 ]  e4 L" c6 ^( d9 @+ ~- D: g' {
3 U4 _  L, l# B+ K7 J+ T
$A000:  镜像选择
0 C! m8 @8 A' Y, J: l: V: [5 O        0:垂直镜像. \1 o% s5 ]* \& v# _" Y9 _
        1:水平镜像* A2 u7 _" N7 B* \$ ]! x
+ X% r/ J9 g3 F3 z5 V
$A001:  SaveRAM 切换
; W+ d$ l) ?" n; ]        0:禁用$6000-$7FFF
* ^; I. D; q1 r5 e) d; M# F        1:启用$6000-$7FFF* C7 w' @8 X2 A3 c* _9 q/ J* n
" m( B# X: H* G; K
$C000:  IRQ计数器; X* _! z# [+ N  _( D
        IRQ计数器的值存储在此处
% ~; L9 f7 n% M# X, i7 S* u2 B4 d) T9 H6 @" z( ?
$C001:  IRQ暂存器; l+ l3 m9 V9 n
        IRQ暂存器的值存储在此处3 T$ V3 t5 B" X8 l

% y  _1 O/ X* g7 m- `! i. \$E000:  IRQ控制计数器04 z4 p+ Q, I4 z2 ]: D  Y
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ
, b5 q* D  z" f* }6 X5 V* u# D2 _& l# X1 J; I! s5 d* z1 r) ]
$E001:  IRQ控制计数器1
! G- S. k0 D7 h' |  f9 x8 _( j3 d* M        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)# @" Y7 Z0 F1 T3 b$ B
/ P) o. N$ Z; \7 m3 y( _/ R* j% o8 q
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:' O( x1 b" t% x- g, A" H) j4 u
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68% J) @: A0 z& i5 N- Y6 e2 @
PHA      累加器A入栈8 A, ]  A) C( [7 e+ s+ g
LDA #$06    设置切bank地址为$8000-$9FFF$ N. ^$ C# W1 X
STA $8000
9 r1 V6 L5 \! rLDA #$0E    将第0x0E号bank切到$8000-$9FFF+ d2 |/ g5 v0 h; A" ^, O
STA $8001
0 r+ P0 |; ~! d" C7 dJSR $8000    跳转到子程序$8000
5 u+ v  `! L, O/ f, BPLA     累加器A出栈9 s  R/ c+ |% F" H

0 m7 O; @: y. W: s% i/ [为何切换的bank号是0x0E呢?
3 `9 V; V: ]- T8 G因为在扩容ROM后,文件PROM结构为:
8 q- \: x& z2 z/ W( `; J原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM) L! y1 w+ j* z4 Q! Y' x% U
* g& @0 J) w' [& n
Mapper 4切bank一次是切8KB,那么文件结构就是:" j4 A4 N' W/ ]: X; Z( ]8 W
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)4 u0 y3 h" U6 q
因此选择扩容的第一个空白bank就是0x0E号bank。( k6 t8 x* [9 }3 @) Q
. Y! J( I. `+ S+ {1 H* d

0 V( u8 d6 O6 V$ ]5 y1 F4 D8 U然后去调试器找RESET中断中可以放下切页程序的地方:) \6 r8 Z5 C& p0 B& P* k1 y
首先长度要小于等于自己写的切页程序。% @9 W/ d3 o' ^' ]/ U! ]. G
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)& z5 r9 w3 A$ w0 F# F1 P
6 l) _4 v2 q: N9 T
然后跳转到$FF7C:7 U/ Z7 g; f; F

# i. a3 [6 _3 w# J. P4 G, a: H2 Z& ~/ |. O* ?$ m
" d; M5 R+ i: Z/ o  m8 W

2 k7 B3 f3 I+ M0 Q0 _复制下可以被替换用于写切页的程序:$ O$ m  R+ F/ ]4 G
先选中,再复制:' S7 v! J2 P- U

6 s1 s( C6 l. R* ^
. K0 u' r1 B- J( _; z- O在Hxd中新建一个文件,把复制的数据粘贴上去:  o- L! l3 h3 R9 v9 g& i) B

) u$ l2 j1 }/ e8 U' w9 a
# F  G0 U% t) x: k9 Z! Y' n7 h* ^' l

' u) Y9 o* b: x) Z8 D: ?然后回到十六进制模拟器:% L9 d1 I) |9 ]4 s( [8 P2 Z
转到$FF7C对应的ROM地址:. }# w, u" _5 T0 }) l$ ]1 n/ l7 L

0 D/ T9 J( B8 G0 E$ T1 M3 A+ ?
0 m3 Z) Y" X3 q( O. }. f3 E然后复制Hxd的切页程序,粘贴到这里:( e& l' G( N; y
% H& @1 |' b  r/ v0 m$ o0 D: h  ~

+ K3 n, L% f+ }% W2 w5 O9 m. q% y$ v9 ^1 }9 h4 u
没有覆盖的用EA覆盖:
* k, M5 \5 u( D2 k' q1 y% W
9 J% N3 h1 A* L, k; h打开调试器可以看到变化:! ~, m* W. y% [5 u2 i5 @5 I
: i- r% h2 W& ^! o' n
然后添加$FF7C执行断点:
- V- L* K6 G- M
7 u0 `+ _: G9 W5 n8 w$ O
( N9 m& X2 p) g! z; }  R" l6 v; q" V; Z0 L! ]% s: T* r
单击运行:
6 y" P6 t! S, L  g; G# }然后程序在$FF7C这里停下来了。
. g* }: L; d  S, U) d+ ?0 g8 `; z1 U2 c
然后单击单步进入慢慢跟踪,直到跳转到$8000:# E# j, D1 |) a% h) V( I. c
0 ~$ B' A# j9 k$ W) n2 c: ~& C
然后打开6502_Simulator:! O0 x9 V" D7 `6 Z

( `! d2 k; {1 T% O8 |+ s再打开我写的数据搬移程序:
- m  v% Q0 s0 o) u  B8 w3 Y3 B  h" E" Q, O7 s: u! q
8 [( E% u8 h$ b0 \
然后修改对应的数据:: k' N$ D- D+ [% B! ]
程序开始地址:修改比如$8100就修改为 .ORG $8100" o! l$ i! Q" E+ e, }
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
4 u6 [. z7 g/ D; H# i5 N: t: u7 y从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82
: b' A9 ], w% v3 u想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$918 A& r; r/ v9 r& x1 _
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。$ S3 U$ U7 J8 p; Z/ R
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。% ^$ h. M; ]* P5 h$ p( K# s7 r
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。6 N6 o: o7 e8 b; }3 D% }
后面的不用管。" `* ~& B( k& l& i8 K* g
+ l0 Y$ Z$ x. I; F
设置完数据后单击编译:$ \0 I  A( x, r1 h8 E

( ^! Q6 L- u* o" r. Z0 }然后保存编译文件:2 Y  ^' R$ ^8 u' f: J$ g" S* Y

% P5 Y7 v4 d, e) p- Z! k选择二进制方式保存:  Q* V. o' c0 M5 a3 M/ G

5 r$ r! `( U, Z7 v4 ]+ H/ M  X$ C
3 l' Q! G6 q, f, j8 O( L用Hxd打开保存的二进制文件:
( K+ g! R+ l, i2 c( j2 g+ |/ x& Q5 P$ q  g! B/ J! @% P
! q( _- V+ A. O7 {8 |
跳转到设置的程序开始$8100:( w- D) e% q; c8 e

; n$ X! t( `  \9 d# Y$ l4 ?
( o7 C, m6 q' |# |  \1 F
6 D% W- Y7 Y/ n( n8 {回到FCEUX,转到NES内存的$8100对应的ROM地址:& H% t! b1 ]) I" `6 U
# ]0 D" {- k# t8 m5 X
4 j+ Z( {: u2 Z" ]; i8 ^2 o
8 c* E/ Q4 A, w4 Y4 v! @
% x" X& L# T6 D1 L5 G# S" @! B
# e9 V4 I. p( ?+ s. _7 L1 L
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
3 I# n6 l  e, C! I1 |- n. E
6 Q6 u! w" }3 I- w, i& d3 u4 O4 F( t; ~8 }. G+ l1 Z; p

" I5 G5 n, |$ o3 P然后转到NES内存的$8000对应的ROM地址:
% u/ f7 O; ]* z) W1 U$ M5 d5 `
( ?% ^+ k3 C# W' W7 R3 o0 ~  {7 h
  L: [+ j2 w) t$ [

, j+ [, g3 _+ [' x7 Q写上如下程序:
0 R7 k2 G4 x( R3 N- v0 JA9 80 8D 01 A0 20 00 81  5 ]3 c( f; X! J: c
LDA #$80
4 }0 C2 n- R6 d8 ~! D9 BSTA $A001 可写方式开启SRAM
' u- x- Y' N! Z- f+ N& HJSR $8100 跳转到子程序$8100
% P! m2 ~  l9 N6 Z$ I; e( k; ^* P6 ?& ?1 k! b+ _
然后把Hxd里被覆盖的程序复制过来粘贴在后面:
" I4 f+ U) @$ F
/ T( B! h1 C. s* ?3 O: y$ G* G$ t. l9 r

3 D, V: x  T; R9 R) z! F. _1 w末尾补上一个0x60:
! K6 E5 W1 L# R* m! X; }. jRTS 子程序返回
% E, T( F; f! U+ i2 [; H1 W: l- g; b
然后单击运行,ROM音乐响起,正常运行:5 [6 [( B& k7 p" w
% k  s$ u1 g# U
5 z( C' n" R+ X* j% _6 _* J, W0 @
然后转到NES地址$7000:. o/ v' E( k0 C
0 C9 E: n% C" X/ d- h
$ p1 \8 @% o" J4 [
  \" J8 X/ G+ E7 s

8 K) d2 U6 W/ N5 ~. k/ G  c( P8 n: z
可以看到,$7000-7FFF都被复制了一片数据。- A! S7 I8 n8 l3 P) l- O; X
测试没有问题,然后保存文件:
3 }) b$ y1 r- T9 T) y* e* g) }
9 a, b9 c% {4 E/ F以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
* f/ _7 R: i9 T9 ^0 M后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。* ]9 ]1 y! X( i; h: i; e: w
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1183 天

[LV.10]以坛为家III

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

签到天数: 1689 天

[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-9-17 06:49 , Processed in 1.078125 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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