EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
* ?9 {9 y  U+ N1 v1 H. ?
- x' y1 _7 V" D& Z. h1 B4 \[FC][SRAM扩容教程(Mapper 4为例)]
$ E$ ^1 |" y% U! f9 C
( Z( U8 a; E4 f时间:2017.4.28
) j: z# T4 {' h5 U* Z$ D作者:FlameCyclone
) [0 d  _# }# i$ Y& N工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator: H& W- |" y5 h4 {  n  |4 }1 M  x3 a
ROM:双截龙2(J).nes2 X  Y& r' t2 p2 S$ R# g1 G0 ^- X
适用:没有使用SRAM的ROM0 T% G3 c2 [" T, K# O
$ @3 W1 ?1 m. ^& @) i3 r
首先用Hxd打开ROM:% }; T" V. i3 G1 V  G# k
/ e4 ]2 m. H& Q/ Z
然后扩容:" G9 Y1 U: n& K8 z+ i* R

3 n  e$ S6 T$ _& d+ W* p! A3 N0 E/ a+ b) r

& k' o3 P$ C+ z
/ |* z6 t/ i" H$ k0 H! Z1 l9 O. t  C; K1 v3 P% w  f( u# V+ z

" B+ w! e# i3 J" x6 q) n4 e# C2 i, W- i, }4 J- t# b

5 }8 Q; E+ K& i2 z5 T# H) u8 M6 Z
; r# z  v0 I% Y" W9 d; @9 Q6 }1 C, b5 _先看看任天堂产品系统文件对NES文件的说明:
) O# q. d" J3 Z1 c3 zNES文件格式
. U7 Y2 b6 Z6 @0 J. K: p.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
0 A1 I  }" w4 b, K/ w$ ]6 l偏移         字节数         内容
. ]) P+ B: a1 @( e3 M- V; N& ~0-3         4         字符串“NES^Z”用来识别.NES文件
: i2 V* J5 w+ G. Q1 n) P4 s4         1         16kB ROM的数目 ( c" y( q8 }: y, Z, K! R
5         1         8kB VROM的数目 + S4 X  l& e) B6 p$ @
6         1         D0:1=垂直镜像,0=水平镜像 ! I! Z, q2 g# o
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF ( S6 i: d7 M+ A) k( i* P2 G* E4 k
                    D2:1=在$7000-$71FF有一个512字节的trainer " U. `& q0 @: }, k  k. s3 `
                    D3:1=4屏幕VRAM布局
: X& ?% @% `. O; P                    D4-D7:ROM Mapper的低4位 ' o0 O0 a$ V, h5 d2 o3 Y3 [# u" y. u
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
$ V' K5 i- c* U6 T                    D4-D7:ROM Mapper的高4位
7 z$ b  l) Q0 ~2 _8-F         8         保留,必须是0 ) i, ^8 K6 S* s4 R  W
16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
( l6 V/ r$ {! U) F, U7 R9 p: A9 P+ K-EOF         8KxN         VROM段, 升序排列 " ?2 `4 Q" Q3 K4 t
6 V" R% r. f/ B/ s' N& \6 i3 y
然后知道这个ROM有0x08个PROM和0x10个VROM
# F* j4 |7 t9 M; x接下来扩展PROM位0x10个:$ G  G' K8 i3 C, F9 p
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):
6 v* f( K3 Q# S3 y. \
9 ]1 h  N9 b! a由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:- G5 M; a" ~2 f8 v: d; a  v) S3 w
最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。6 c  p" t5 A2 R1 s) j
于是可以得到双截龙2的是:
( \7 H9 J" `- k(8-1)x 0x4000 + 0x10 = 1C010。4 \& U2 u/ _; ~5 ?, {
然后跳转到1C010:
1 u5 ~5 n& t- ~% O
: g- D1 j, E" ?$ ^; L9 N( V5 U3 P! g$ w: ^7 u8 e/ b

$ m; R, c1 H0 e+ V然后插入0x20000字节的FF:9 Y5 P3 o6 W8 w

: s: E+ ?7 S$ _8 j3 d
* Q2 Y2 }. p. ~5 Y' }/ s6 i2 {, |5 H5 h- q
# N  C% R) a. ]& L5 G; t( S然后保存:
  }( R! K9 e  A8 e& G/ o% ?( ?" V
6 J: m( @) h4 {' R- P2 E; ^, ^
1 k6 h; R" U1 F4 r, @- j5 l- w6 O用FCEUX打开正常运行:) E- Y+ W; x! [
8 @* U, e" Q( @! \6 r& ]3 X* |
查看文件信息:
3 A/ b! r. N2 }: x
6 d2 G5 b7 ~: ^3 ^2 G, ]' P- A
4 f+ f# F3 d) V6 y& w6 b接下来切页:/ Y# L; \6 B% @1 T& H
先打开十六进制编辑器:
1 J) r7 D+ {6 ?/ ?! \) _% \; |$ x& ?
拉到滑块最后,看看重启中断% R% ^# A: U: U( I8 F
中断地址         中断         优先权 % l7 u9 T6 D  I: T( Y
$FFFA         NMI         中 - K  |: O$ ^. N
$FFFC         RESET         高
* \" r' G: s5 d$ |& d6 W$FFFE         IRQ/BRK         低
( ]4 ?5 V. v" C; {$ z+ h( @  |6 Z
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。& r: Y, I  A- P2 m( ?
由此可知双截龙2的RESET中断$FF65。
: |, f* a& i7 Q# ?5 g( z- a9 x接下来添加$FF65的执行断点:4 R/ j, h0 g% g) ~3 s& p9 D
打开调试器:
' b5 z4 K7 z; |8 @) O) T/ F) A6 ~0 h) n
添加$FF65的执行断点:
4 J1 l6 s" s; L# V/ m1 i, h
% t9 f# L2 W7 S
+ {7 O: D4 n# L4 Z, A
& m4 U* a& f* z2 f单击确定:
. v. y6 S" X, {1 Q% ?# T5 R( {& F( p+ Z* i  W0 q4 P% q% x0 f
2 P2 |$ w! P, Y+ I! t3 u3 H: b
然后重启ROM:
$ b9 f# a8 ^% x9 d& i) {: b: p% v' v" Z0 k
调试器此时弹出来:, u3 r5 h3 x! x! I5 [

% x- [: m  M, w4 ?然后打开Hxd,写一段mapper 4的切换bank程序:
! v* q  ?6 J" c: Y" C! b, R9 b1 Z先看看mapper 4的说明文档:
6 V# l: B! f3 O1 Y- p, OMapper 4
# [# U, a2 _, @- _7 \' x5 z# _/ D) b5 ?. [) L# m
$8000:  模式号! G( q, S* A8 p( t% v
        位D0-D2:+ W3 t9 ?0 n5 D1 T  M
        0:选择2KB的VROM存储体映射到PPU的$00003 G& @& e; @* U. C, w3 q
        1:选择2KB的VROM存储体映射到PPU的$0800
5 D* p9 _. j6 x+ [4 o1 C% f7 H        2:选择1KB的VROM存储体映射到PPU的$1000; o3 f' u' B$ }! z& f6 t8 _; d
        3:选择1KB的VROM存储体映射到PPU的$1400
% k0 i! `/ T5 l+ v8 @        4:选择1KB的VROM存储体映射到PPU的$1800
# W' b4 E) h$ V2 [4 {; q2 D        5:选择1KB的VROM存储体映射到PPU的$1C00
& W) ~# J7 d0 s  Q        6:选择8KB的ROM存储体映射到$8000
5 @! S. S8 W& {5 A. `. P1 c+ g, W        7:选择8KB的ROM存储体映射到$A000+ X$ a* y  A2 n2 [
        位D6:8 p6 I) J3 g$ l! @7 \
        0:允许擦写$8000和$A0003 b% T, T) F& S+ D; P& w' P9 l
        1:允许擦写$A000和$C000" f7 U5 S8 e  l
        位D7:
' n# j) m5 W* P        0:模式号D0-D2使用普通地址
8 z9 k- X* _/ M: O" D( e4 p3 y4 K        1:模式号D0-D2地址异或$1000
  C5 R! a" r! g
0 h8 o, I( Z/ M# @2 J$8001:  模式页面号
6 _; w7 k9 N6 ^; {4 I1 @        写入一个数(00-07),切换存储体到对应地址- o. b# n; p6 D% \0 I" o, Z
! h5 l* i5 w* f9 q3 J
$A000:  镜像选择" g4 `6 ~0 j/ |7 d" v, @: A* m! o
        0:垂直镜像
6 y4 V9 o' e' z  F$ O+ z! a        1:水平镜像
' t# W9 n8 O+ k  Q$ J
- s. O3 X' @0 e/ f$A001:  SaveRAM 切换
' f0 R, Z0 Z: O$ ^        0:禁用$6000-$7FFF
# s2 b9 W- x: q" F/ S8 ^" [, B        1:启用$6000-$7FFF
- \. y" f' W/ a4 V6 a5 g4 ^
# d/ |8 v# g+ A$C000:  IRQ计数器
: Z& M& g3 N2 E0 P        IRQ计数器的值存储在此处7 ?  [: l3 x, x8 {  H4 k2 h
. D8 l9 j+ j  V3 N( o1 x
$C001:  IRQ暂存器# ]8 ?, I# o) I+ n# ?( W4 A! b# o! y
        IRQ暂存器的值存储在此处/ r1 K' u* C  C3 L0 Z9 E

; t6 Q6 L( a1 l7 l; v2 o) a  I: c$E000:  IRQ控制计数器08 x2 N8 `8 a; U  ?
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ7 Q7 ?+ V) G. _8 |2 X/ s& B* r
2 _3 I( `6 p& P& p
$E001:  IRQ控制计数器1  `  F) b) X3 p+ P+ y% t: Y/ e
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)# E# R: [! k6 @2 Q
. @, m; _# ~7 d
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
1 @% ~3 G# g$ U( }* q9 g48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68
' g, }5 j1 I0 UPHA      累加器A入栈0 t6 k3 z2 d  t$ U7 j( v2 `- w/ F$ _
LDA #$06    设置切bank地址为$8000-$9FFF
/ [5 c/ @, H% DSTA $8000* R  ^* m( m0 v6 C
LDA #$0E    将第0x0E号bank切到$8000-$9FFF0 o/ G2 J$ ~: i# d
STA $8001
& r6 S+ l$ g4 g- mJSR $8000    跳转到子程序$8000* E! S, N# k' Z8 u
PLA     累加器A出栈
+ ?5 b- }7 @. }4 |! W) a
# y1 e& }' n! s4 D( U为何切换的bank号是0x0E呢?2 g$ ^$ Q& @8 G4 c& Z9 s& R
因为在扩容ROM后,文件PROM结构为:- d* U& j5 g& {
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM7 ~$ H5 {& n& K, n  h/ ^1 R: P$ ]

( N) _7 _3 i0 [: c" |# n2 QMapper 4切bank一次是切8KB,那么文件结构就是:. L5 v. G0 n+ b% a/ d
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)$ G9 N2 k2 h1 t3 v& K% ^3 L
因此选择扩容的第一个空白bank就是0x0E号bank。
& f- u( _, ~- `- }. q6 \3 v8 N( F  q( L  H, X
) v, J! I% D7 ]: u
然后去调试器找RESET中断中可以放下切页程序的地方:+ s; j( V- w0 n3 f' k( s; w
首先长度要小于等于自己写的切页程序。
/ |" M* I* ^5 H  P1 t可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
' R! e0 e! t" J5 M! U
& x5 f7 A) `  r5 o5 ~然后跳转到$FF7C:! A% C; c- P( ^$ c! j5 z' Q

1 P2 I# D- F. n( |3 n+ ?" Z
! j8 R, B4 d  i1 a6 {+ P6 ?, i$ Y/ m
7 Z- g( O# Z, t' Q* ?$ j& U
' S5 P* m! b6 D复制下可以被替换用于写切页的程序:
, Z/ p# ^4 n+ P' B; o先选中,再复制:
6 T' n# o' T+ w$ X- j! c
2 s+ k1 Z6 v: W
" J* p! G# c* K  I6 h  |8 @在Hxd中新建一个文件,把复制的数据粘贴上去:
5 j; P) _! R2 x; N$ J0 j- |) T: ?& I
- L* k: Y- j; d6 e9 Z

: G3 B1 X( }% a" T3 f5 w" j$ |' V: D+ w; D" u  H; Q
然后回到十六进制模拟器:( }) z+ j3 V" i9 d
转到$FF7C对应的ROM地址:7 F) O) L6 U5 }; I8 F/ J

6 L" t* o& Y  ^+ w" a: `  z* x; Q9 M0 ?: U" k) G5 w
然后复制Hxd的切页程序,粘贴到这里:
7 C, ?, u! D; {6 {8 y' A6 v; m9 G' J4 G/ v  ]; {

/ c% k6 S' N( a" {* T. m/ J8 z
4 B7 `) o8 f1 l/ Q- R3 s没有覆盖的用EA覆盖:
7 W1 A- M. s6 i  [
, G4 Z. ?. E# k- r7 _打开调试器可以看到变化:% E- E, L5 y: p; Q

8 U8 Y1 B' V, S6 R然后添加$FF7C执行断点:
# Z. I/ F  ^" Z# x! F7 f
8 v% J+ l0 T$ }7 m+ h: d0 j7 o$ w; u; e" F9 u# M" q9 Y( S2 J% y

4 U5 o; K3 A' ]: K/ k单击运行:# M  K  r3 h8 g* |: _& Q2 T, Z
然后程序在$FF7C这里停下来了。7 T4 O) p9 B' ?2 n, k8 ]
! s7 o& F) ?* w4 |7 _. q
然后单击单步进入慢慢跟踪,直到跳转到$8000:3 Q, }; j- ?2 e( m$ N5 c: R
7 Z& c, r& x; M& p1 s; j3 J
然后打开6502_Simulator:
( E8 z" H( g0 ]& A, O, x
, l( G3 m- k, U再打开我写的数据搬移程序:
- C) K2 {7 p7 U; S  Z: t
8 m- ^6 n; j7 W
, E/ v; h. p- N! ~  b然后修改对应的数据:: T4 t" z0 r7 m* K
程序开始地址:修改比如$8100就修改为 .ORG $8100( t$ \/ R! D$ t% B! s- ?3 c2 h5 j' r
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70$ A0 R. N1 S( E$ y: _
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82+ z" A0 y/ V8 I1 Q0 f' [) e4 u+ G
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
- M+ H# v" k- @% D7 h# i" J也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。
7 [* m# F# _5 z+ ~! Q如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
  ?% _% f3 K6 N" e中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
- q! e$ \0 \+ U8 m. ^后面的不用管。
! u9 S3 Q1 z3 W  R
8 L5 |8 \2 G, y& `3 ~" W4 P/ p( g/ R, D& s设置完数据后单击编译:
1 U; U+ x- _  o5 u0 G1 B. w- `( a! O0 k/ {% y8 f
然后保存编译文件:$ E7 L1 Z  S3 ?, M7 ~) v
7 {* {9 x: i7 f2 [6 k  A
选择二进制方式保存:
" C5 U# Y- k7 \4 G% |! }5 g; G0 c$ e3 d
$ t$ O7 N6 ?! w* _
用Hxd打开保存的二进制文件:* R' E% m. }! A& z3 G

7 I- g2 I, p0 {/ ^& X9 A
' U: S' g% ]# ~, ^; X- \跳转到设置的程序开始$8100:/ n& Z' m/ }% d  x2 U% Y
/ Z7 K; [1 C! i2 N' H, d
9 w' g" N1 g! f$ L2 I' c5 b

2 g/ B. _1 H2 [/ q回到FCEUX,转到NES内存的$8100对应的ROM地址:, K& `9 ~3 M9 t: s; T

. m) e$ {, ]1 x; j) e' O
% L8 Y& P# p2 m* E( D8 L- e  z7 M, R5 t; I! r& ]9 w; _

! z! n5 W) r: r- E: ~, n4 s: \& {$ N7 `" d) f0 h
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:- F' A( G0 g+ }% K5 i
. v0 J& g3 X/ Y7 y: _" ~

1 t0 Z5 {, c' z, q
+ h0 N* P0 I4 G4 R) i0 S: I7 Z然后转到NES内存的$8000对应的ROM地址:& k1 M. i: m" L) s! |& D
% S$ a, b  ~' v" S1 |2 ?
3 K: F3 ~% @! s4 p' G6 V. C
- V1 c' B3 q0 q; d5 A4 X

; r0 ?* v* x6 S, i$ `8 `写上如下程序:8 [4 n) L! |  x/ h; q, G) T
A9 80 8D 01 A0 20 00 81  + a& T& z8 Q' w% N) @, w
LDA #$804 P& B. V% Q" R1 _; z! a/ l
STA $A001 可写方式开启SRAM0 X$ @: r+ j  f3 `5 ^4 d
JSR $8100 跳转到子程序$8100
" D- {! v- h0 E0 F0 b  z2 a. h- T, N8 b4 @
然后把Hxd里被覆盖的程序复制过来粘贴在后面:
) u) y3 j1 I8 O' y( i6 K# m7 a: v  k' l% e- U, p8 y
1 R! Q/ `& s7 n% }+ S( y; n

5 F9 f  {. ]  |' ]* L: R; E末尾补上一个0x60:
0 v+ a& }% o4 p# iRTS 子程序返回( S$ F* Z" b, G$ Q9 H7 S* [- K

2 `% b6 w3 B" b( c4 g: b  N( s然后单击运行,ROM音乐响起,正常运行:6 ?, B2 V3 R' R: ?
% _- ^9 C* O* b0 d* U4 [7 `+ U2 H

. t3 l/ Z" l) D! f然后转到NES地址$7000:; Q5 Q+ v8 T/ Y% |# m' S

+ B6 n; Q+ G% E; o  C% y
2 Z8 k% m- Z) U0 ]
+ e* n7 v. N  i  V
* n( k9 ^7 z0 z# J  |+ o1 b9 b7 M- K+ i/ M- J
可以看到,$7000-7FFF都被复制了一片数据。# r2 b4 I* C9 t0 }$ G$ P
测试没有问题,然后保存文件:" b2 t& A' _/ D" B2 ^
2 ^" ~2 W7 ]+ Z; L+ P+ J$ \! u
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
& _* _/ T7 e4 T# L6 L9 j" {后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
3 ]9 A8 p! Q: c, d! K
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1183 天

[LV.10]以坛为家III

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

签到天数: 1737 天

[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-11-4 14:17 , Processed in 1.079101 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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