EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 % J+ w" p, r* y( A

  l0 ^7 p) S+ Q: p* u, l& ?[FC][SRAM扩容教程(Mapper 4为例)]# e; N6 E" ^) R3 c: k' c
0 y% E9 P3 Q, o0 N
时间:2017.4.28
" t5 k5 l$ w7 K4 S, R& z作者:FlameCyclone
% {( s) }3 J# e; w工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
0 h3 E) ]0 S8 P+ c% gROM:双截龙2(J).nes
7 ?4 ?, i2 z) C: \适用:没有使用SRAM的ROM
6 t3 Z  c; Q, O$ b1 S  S3 t) r- M
, V9 b3 B0 J8 e1 B) U首先用Hxd打开ROM:4 u0 [3 z7 z0 g" j7 u$ \
( |+ Z' u( H2 [
然后扩容:
+ g# E- ]5 k+ z$ t) ~; ~, g/ h8 T- ~3 C
) u% x* A! t* f' _. i5 Q1 B1 S
0 ]) r5 i" s; b& T7 r

# C6 ^0 w' K: W. y' u$ w2 [' V
$ {+ i& z. z3 \; h0 h7 Z* M+ m2 _/ O% ?

+ O* e, E2 [) Q8 ]' G. X! V; H  R' p7 o8 R% r0 ?2 P, g& c4 Z- D
$ _& m) v: m( U8 _* z* T5 n# h
先看看任天堂产品系统文件对NES文件的说明:
* X0 n7 @  K. ANES文件格式7 Z4 g) u4 e/ n6 X
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 $ D0 ]: n) U6 A, H. l7 g; X" Z; b
偏移         字节数         内容
8 }8 |& N6 h) D- d+ ^. x0-3         4         字符串“NES^Z”用来识别.NES文件
/ B. O* `7 w# [  ]  ~1 z5 ~! `6 T4         1         16kB ROM的数目 $ D1 t" U. T' I9 b
5         1         8kB VROM的数目 1 T0 V" U9 y9 V" g6 H
6         1         D0:1=垂直镜像,0=水平镜像 ( ~6 v& V) O; j7 @
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
. }# [6 r  M! \, A# m                    D2:1=在$7000-$71FF有一个512字节的trainer 1 \2 h6 Y( t7 b4 T9 D& J
                    D3:1=4屏幕VRAM布局
4 s. G) P8 o4 V; m  _! v                    D4-D7:ROM Mapper的低4位
2 ]/ L. G) ?& m1 j( m7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
% [* c  J7 ?9 p3 ~; O                    D4-D7:ROM Mapper的高4位 . E; S* k3 e- Z: q% Q
8-F         8         保留,必须是0 ; ^; j6 ?  J# Q% O% {7 ^
16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
3 t0 {7 u5 n- V' T-EOF         8KxN         VROM段, 升序排列
2 I) E; ^  O9 _8 r& G0 c* a  H( W2 d% c" ^7 j, b% P& |! o
然后知道这个ROM有0x08个PROM和0x10个VROM
4 i$ Q( y% X  q, ?接下来扩展PROM位0x10个:# G; m& t4 w0 k, Y
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):( ?+ W2 U) g/ ^3 [$ L  f/ g

* i1 v4 u; M! O, y4 |  Z  Y由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
( a! H0 `* H, P7 d最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。
8 C7 W, z# I/ q2 I8 a& J于是可以得到双截龙2的是:
2 {: X# t8 Q5 a$ ^% P, f(8-1)x 0x4000 + 0x10 = 1C010。5 z- S/ w1 M' S$ Y: `( P* \
然后跳转到1C010:) X4 F* D  P7 x. m
8 G/ Y! L  ?4 G+ `7 j% I' q5 T

7 n; ]" J5 e* O# R! H( X/ y+ x, i1 p% f' F& T4 R# o
然后插入0x20000字节的FF:
* n0 u4 r  H7 m" C) U; W9 w
# c, ~, d+ f* j% a# J4 J" Q3 H: e9 ~7 B7 i4 b
# }: C4 o5 ^" F
然后保存:9 p9 J% R# c$ O

/ d$ \! P1 K+ f5 @& I
9 k. b( Z% E1 j用FCEUX打开正常运行:
5 P4 O, N- w( {5 _" j+ I' _8 N! a
; v5 d, X: b  E, b+ \4 F* f8 |查看文件信息:
' Y' @" M4 {, s% S7 E- ^3 J/ m
/ s( d8 d3 @' L% e
6 B# m3 m+ ^1 {' u) J接下来切页:9 w1 H. J$ r) b8 p! ]
先打开十六进制编辑器:
' J4 `' f1 v+ d4 K( t
, A6 A+ R1 Z* r; u拉到滑块最后,看看重启中断
* I3 ~6 q+ z  t  X中断地址         中断         优先权 - O5 A0 ~) o- L& \' h) n/ V
$FFFA         NMI         中
/ Y. w/ b# E! R) ?9 l$FFFC         RESET         高
. o1 m# y2 T) e6 G$FFFE         IRQ/BRK         低 0 w% m( ~  I. o) E) t

' D( ]5 g0 `  d1 Q- u- D+ nRESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。$ x) z  ~: g4 }
由此可知双截龙2的RESET中断$FF65。
( ?+ A& p! n' [1 H# Z. N接下来添加$FF65的执行断点:7 w0 D! h5 k5 [1 L2 K# ^3 z0 Z
打开调试器:' T5 M0 C& ?3 `9 g

3 a3 N7 x6 C$ g. ~6 p. U添加$FF65的执行断点:8 T; s1 k; F2 }3 N' l
, u! ^0 B3 h# \

0 [# A- I, K' }4 g6 l" r
* @0 Q$ E8 K+ F4 q6 ^单击确定:6 N+ y1 [9 k) s6 l+ z$ c% |

# g2 j  z+ V" @- T4 j0 V; O7 }+ @
' Y( l9 u: M, C' l然后重启ROM:7 M+ X" ?2 B, k- L) J: S) a' z

8 O# n. z% A1 ~7 t+ e% w调试器此时弹出来:9 o: l% ^  k* B+ N
" a& E2 i4 B' N7 X& X1 K
然后打开Hxd,写一段mapper 4的切换bank程序:
& h; }& b5 H* a- Q先看看mapper 4的说明文档:3 f* }6 T0 K' J
Mapper 4& \& M  |* R) N3 Q; A! o% t6 F

# b# D3 n( \- I" u7 F$8000:  模式号* a6 |# C. C9 `6 u1 y
        位D0-D2:
1 k2 c2 Q$ @/ N. A9 b6 d; ~4 l9 Q        0:选择2KB的VROM存储体映射到PPU的$0000
9 c) |( y8 I; ]- z7 v' p        1:选择2KB的VROM存储体映射到PPU的$08008 ^& r0 s. C+ g
        2:选择1KB的VROM存储体映射到PPU的$1000
: g! C9 n0 L- \" q* A9 ~( b        3:选择1KB的VROM存储体映射到PPU的$1400
( z- a/ W  G: u1 H        4:选择1KB的VROM存储体映射到PPU的$1800
3 ]6 S# x, Z1 q7 g# Y& T; J1 g8 m) f8 j% g        5:选择1KB的VROM存储体映射到PPU的$1C005 n) j- t; ~, h7 y( i! g: X; F& ~
        6:选择8KB的ROM存储体映射到$8000
& ^" m  u( W! W- I) B; G6 V        7:选择8KB的ROM存储体映射到$A000; Z% G" w. T/ o' A5 d
        位D6:
; j' E0 R; B" \        0:允许擦写$8000和$A000
7 w7 N1 f2 U7 u) w. W% W        1:允许擦写$A000和$C000
+ |* J; O' k; Q5 @' s        位D7:+ W5 K* {: h+ d
        0:模式号D0-D2使用普通地址  H8 k6 J# c4 Y1 {& \% A
        1:模式号D0-D2地址异或$1000
- O. d1 p$ c8 o2 H% ]/ S
4 y7 S+ A( d; J2 ^# j  L7 T$8001:  模式页面号
1 M8 ?# ~' O9 w) K        写入一个数(00-07),切换存储体到对应地址- _, a$ E" }8 G1 u* i7 ?& m9 v) F
' A( G% W/ \% S1 P6 o- P
$A000:  镜像选择
9 U% T* r5 O6 T' {: O/ J- t9 R        0:垂直镜像' C- c/ A3 Y/ ?' H- \
        1:水平镜像+ ?& K: U5 {! c2 g: G0 O* T( r
3 q7 P# I) V7 h
$A001:  SaveRAM 切换( s: O- I6 s1 a/ H& c
        0:禁用$6000-$7FFF" y+ |( B( C3 r1 b; \& s1 R' I* w
        1:启用$6000-$7FFF
" i) U: P7 r# }5 O" @, [9 P- ~4 F- j. M1 l
$C000:  IRQ计数器
9 e5 k- t% p0 z4 i        IRQ计数器的值存储在此处
% ^2 U# J3 L1 Q; F' j  o1 O6 @2 c' n6 m- r5 U& x" |0 ]
$C001:  IRQ暂存器
4 z# a; X9 g! F  |' Y        IRQ暂存器的值存储在此处
; A- |6 k7 G2 ?+ w3 `" T2 p% A- @4 F- y) b$ s! o
$E000:  IRQ控制计数器0
8 n' D0 s2 X6 T/ I# \        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ
1 g  l% m) I& C- w+ A9 y; E
2 Z( A# e  a) g& q$E001:  IRQ控制计数器1: o# v  ^6 J5 [
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)5 y' s3 u  j8 H* K

  e  ]: X1 O' w& c那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
3 }- k8 W/ n* T48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68& d9 W9 C7 C0 {8 \, q2 _, T
PHA      累加器A入栈8 G/ D, h: q2 q, H# A. U
LDA #$06    设置切bank地址为$8000-$9FFF
7 W* r2 |4 j& r. r6 s* s( R6 iSTA $8000
& b; }+ m% m' @/ P  `% D9 l5 SLDA #$0E    将第0x0E号bank切到$8000-$9FFF
' A% V9 U0 |' g$ p+ dSTA $80012 _0 k% R. x5 }8 `$ W
JSR $8000    跳转到子程序$8000, V, v- {% C3 H; O# H& ~
PLA     累加器A出栈3 |1 @' [- ~( ~/ C5 x
8 O# C# }6 F. @  {3 ^" n
为何切换的bank号是0x0E呢?3 T' Z* q' Z! s- c/ o0 f
因为在扩容ROM后,文件PROM结构为:
; W' _' f' S, S- B7 M: _4 |5 p原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
+ u% N: v5 S$ W
: [7 n4 |0 r5 e" N5 wMapper 4切bank一次是切8KB,那么文件结构就是:
7 @, z; E4 [  Q2 r! ]! N: K: ?原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
5 n$ w- a1 p: n& @$ h因此选择扩容的第一个空白bank就是0x0E号bank。; A6 x9 W- A' H, g
5 |2 e' k/ X, B  j7 O
2 ?2 H5 F5 b$ H! n) ]
然后去调试器找RESET中断中可以放下切页程序的地方:
) N5 g& b: y) ~6 d首先长度要小于等于自己写的切页程序。( o9 I+ x$ U9 _# C
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)+ m, X1 Q9 s$ x5 x: Z+ {$ @! l

9 o9 w: @; t& Y, @. `然后跳转到$FF7C:# r2 P# m) m& M( B7 D; p/ D: m
; d9 W& k# d5 p( R

- G1 Z0 M* n- S8 h# P) g% i" c: z8 J% ]+ S* _/ R

0 |( a6 Y9 i; b" Z复制下可以被替换用于写切页的程序:/ K2 {, }3 T/ c& L, j$ `% L
先选中,再复制:1 f' D' x8 D) k( t, S% E7 a

; Q" `! L( `1 u$ x$ ^6 q+ Y! z) A7 R0 }& o6 j" a4 q. {* V/ g
在Hxd中新建一个文件,把复制的数据粘贴上去:. ~4 Y5 _+ N) Q' ^
3 h4 Y) y1 z+ b/ J0 U1 N: ~
2 ]$ U  `% t6 O/ p9 ?* d
8 ^+ ^' O+ R$ A7 W4 l) r  h
5 H2 [- }' i# x  W2 i6 k, Z
然后回到十六进制模拟器:
9 i: ^4 N4 b7 G( N6 G1 \转到$FF7C对应的ROM地址:
6 g. M  P: p3 r/ [* V
" W4 O. l' d: E# a& g  ~; R7 |: J0 X0 w; z/ k3 K( Z( a4 j+ ]) I, y
然后复制Hxd的切页程序,粘贴到这里:7 F6 u& b" N5 t
$ w: Y# f  w' n9 R# ~

9 G) j* h: H2 D* N& J- v. a- F- @2 M/ \$ k% B& [2 P0 B
没有覆盖的用EA覆盖:+ i! F  u, ^8 J

9 u# }. {6 D' ~5 y6 D打开调试器可以看到变化:
. X# f) m, Q; Y& Z" A
7 c7 N2 [# U" @然后添加$FF7C执行断点:  H  m/ E) H* W( S3 B
# h# ~8 o; w0 e, G9 r. i, R  h
& k& t2 c& o1 s3 y5 u% ~

* |4 D% g8 b) r/ z' R8 w* N单击运行:
7 \8 o7 H* n( L7 W然后程序在$FF7C这里停下来了。6 p5 ~! v- X; l/ Y8 M# y: c

' o' J* }" l: B8 I, u! x! {0 W然后单击单步进入慢慢跟踪,直到跳转到$8000:
8 q  C) e$ ?+ j  H5 U0 Q- X$ ^0 E7 {/ F( g" G) a% `: Z
然后打开6502_Simulator:/ Y# _- \* u/ ?% k# t; S* a
7 v- e1 V, [8 r9 n! j% ~
再打开我写的数据搬移程序:
& v* ?3 f. h# S& h; @5 K" c" _0 V, i& r3 O

0 S1 O/ U4 J$ G6 F, f然后修改对应的数据:+ ]3 v3 n7 }) g4 H# A
程序开始地址:修改比如$8100就修改为 .ORG $8100. }! W/ a. _& k
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
8 J  {; M; Z% T8 K- i从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82
. I, U4 c) r4 s1 Z& [. o想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91; Q9 z' W/ r+ K0 i6 O
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。
4 T+ `$ [" v) [  E( i% c5 x# b" j如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。# y3 P  K0 }! L
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
% n& s- M* N$ m7 j后面的不用管。: ?7 k7 r5 S0 Q9 j* ^

8 e5 {/ i) Y- R7 u设置完数据后单击编译:
% p1 R- m0 C1 |7 U3 ^' b
  D! f% K! ]# X; h* K; v# \: `: d然后保存编译文件:! X6 F, p# e( d% M8 O
' J% z" A) l! f# U" b6 D
选择二进制方式保存:
- x$ l+ C% \" ?1 Y  E  \1 |$ |8 S
6 Z# o+ c4 ?+ E0 W0 c4 k" U& Q" l' G. {5 J
用Hxd打开保存的二进制文件:+ k: c0 f, m: H- g' m" p
1 J" l3 W) P- o# s4 e9 {! E

  u8 i2 g' i6 Y0 d- ]5 _$ |) N跳转到设置的程序开始$8100:
) h" \4 O- w6 R. h- l  L: ]( D+ E, _; C0 x* t/ [  B& z
  K9 I; F  L3 L# k! w1 g$ f2 q  y

9 K* C5 V8 N7 V- T回到FCEUX,转到NES内存的$8100对应的ROM地址:
+ m6 Y0 P8 F8 S
) i  Z# [. K( q7 E) e  v$ ?. O! K. _! E# v6 I# P
8 C# U$ U% |% f/ u% W9 r0 ~% K
6 Z  h5 O1 R8 F. f' D8 q& b

  C/ E3 W, x! u4 y- x然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:, u, Q  {6 L" @5 ?; m" }$ m
9 E$ s% N8 |! O1 {
% m0 j( R& ]: l) s

$ Q- T: g3 L! t然后转到NES内存的$8000对应的ROM地址:
/ ^* S! Q3 x" g$ i6 J
; F" P" q. x5 L0 a, F% J
" u0 L! G2 R& r$ D& b: ]- g1 \$ ]; _4 b8 |2 i: R, ]4 Z% z
1 P, }- x7 b$ _; d9 ~1 z% C
写上如下程序:
3 A, ^. Y% g$ J5 MA9 80 8D 01 A0 20 00 81  
4 j# W8 O$ K8 T/ G9 o  R, Z( `LDA #$80
6 ]% G  ~: ^6 V" pSTA $A001 可写方式开启SRAM
4 J8 v9 e5 i' k/ ?0 }$ {6 \JSR $8100 跳转到子程序$8100
) m% i9 y, _6 c/ g$ t4 z/ y4 x0 q
  ?: H# t6 f( a然后把Hxd里被覆盖的程序复制过来粘贴在后面:1 S/ l; W9 o; R: V+ M5 n! K

8 i: @0 \9 O) {/ ?0 ~& s. T' [7 x0 e  ^) V) y
" R4 z: `) h' U! \" I
末尾补上一个0x60:; ?' o* @8 ]8 v: ~1 f, D
RTS 子程序返回% ]" `$ y" C' C$ j) j* E. S& v+ C

& i6 {! `, }- \: ~  u9 f然后单击运行,ROM音乐响起,正常运行:
  [& @8 ?8 ^7 B# e& ?+ Y$ T: T' s3 ]% N* f  Y. f9 d
; g) s; N2 t& y9 E; Z6 Z
然后转到NES地址$7000:  ^6 O' E* s7 G) x0 I  I* A( i

7 K+ v( q% E" d8 e* ], f
* _7 }8 I( N# R' z# o8 t. q  q% \" z( n/ ?

# B/ ^1 X) M  O+ J% |! x2 b' f3 P" k9 v0 u4 `- b
可以看到,$7000-7FFF都被复制了一片数据。
7 b9 N/ y) I3 U. _# q9 a3 }测试没有问题,然后保存文件:- p! k  n% F# Q. s4 y- t4 X
8 i  J% }) P: T3 E& U- H
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。+ N% _/ X  n- K0 t( x
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
# i1 l  R. G: n$ ~: ]
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1183 天

[LV.10]以坛为家III

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

签到天数: 1669 天

[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-8-27 17:48 , Processed in 1.072266 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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