EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 : E5 O2 s; Z* ]# x

% U8 @) f  W6 H" q$ ^[FC][SRAM扩容教程(Mapper 4为例)]
# E4 ?3 v( I) u* e; z$ ]1 h( R  n7 B  m! \
时间:2017.4.28
: J) ]4 q$ ^4 D! a4 n: H: R作者:FlameCyclone: Y9 L* Y3 f! l/ E  |
工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
& o% b1 ~. r: u! MROM:双截龙2(J).nes: _! Q' a4 I8 Y8 b: c: M3 d
适用:没有使用SRAM的ROM
$ ~% g7 j9 A0 {; I/ }- t) w+ {9 O
, z: ^! o3 I  W# u6 l6 K3 S6 R' `首先用Hxd打开ROM:
9 ~' B5 n/ j7 Q$ R1 L
8 L* j8 ^" x' `  R; C! w然后扩容:6 r, k2 \1 v( e" y. j' j" U( t
. F0 O' o% q! g, b  d, K: D  k
, z$ h* k" j) e. r$ R. O& a

0 \) M& s3 ~5 n) c* d/ W2 H
! k# K) Z+ z0 B5 S% E& h9 u# `9 L1 A  o

& p+ e" `/ A8 j. @7 J9 ~7 f+ T0 x- @' `4 |
% L) x. k% d/ U. N) p

$ F4 Y0 u# Y3 ^先看看任天堂产品系统文件对NES文件的说明:# ^+ y3 h$ d8 Y7 |
NES文件格式( ]: `  ]# q: B/ @1 V+ s3 b3 x
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
) }9 T8 K& `7 Q, m" i; @偏移         字节数         内容 % E6 z' S. {- |/ x* Y* t8 Q
0-3         4         字符串“NES^Z”用来识别.NES文件
' R; C! C$ v& b4         1         16kB ROM的数目
, _, S" V$ n# B6 A5 a5 B. z5         1         8kB VROM的数目 1 \; F, F8 Y8 |/ o
6         1         D0:1=垂直镜像,0=水平镜像 7 U9 P0 K# }; X& O' F
                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
, d# e1 q, ~/ U  J/ F4 G9 f! f  g                    D2:1=在$7000-$71FF有一个512字节的trainer $ x) }' Y: O" Z8 ]% O
                    D3:1=4屏幕VRAM布局 $ \* L. s' m: d# v, |9 @' B0 l" [
                    D4-D7:ROM Mapper的低4位
3 p7 {2 y6 k9 L& H7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^) ( C' ~% `% D0 d+ U7 q2 w
                    D4-D7:ROM Mapper的高4位 ; v: c0 T3 A2 Q
8-F         8         保留,必须是0
7 w2 Q( u! n6 i5 K16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前 & M* n0 v6 E$ R7 I" o/ h3 P7 w
-EOF         8KxN         VROM段, 升序排列
7 E+ _" u/ l. O! g) ^: B# A0 _
: A4 |1 z) C( S然后知道这个ROM有0x08个PROM和0x10个VROM; T- e/ D. X) X9 |
接下来扩展PROM位0x10个:
0 Q0 G: y$ e8 {; W9 G+ y) w8 J& Q1 p9 h先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):+ q" Z6 m# G3 {$ e- [' ~7 c1 }

& a' _: l# R  L8 x1 D由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
) V: P! L4 a( B' ?. N1 Y  _) \最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。
' B+ J! x4 z. z6 B于是可以得到双截龙2的是:3 C; S& ^! W- v) ?
(8-1)x 0x4000 + 0x10 = 1C010。- I% l" N) r8 T
然后跳转到1C010:" s" V8 ]1 B6 X2 \* W& n, W. Z

) M' _7 P- c, S4 l7 B% J1 o6 ]8 \' i: s

2 }8 Z, o0 t6 a然后插入0x20000字节的FF:
' D1 ?+ D- d* J1 K! P+ W3 G4 T; H9 t# r7 W
7 N+ |/ L: g6 U0 G

' B2 H# h7 D# y* R- w& K; _/ K9 g9 o然后保存:: Z4 u( p% O6 V1 b9 r! u
, y3 d3 @4 c# @
: d3 t! y; j* s) s) m  k! p6 \6 M
用FCEUX打开正常运行:
, [" d$ Y! x& }9 h6 r2 m( l! z4 q) [$ h$ J# ]" ?
查看文件信息:  i$ Q  \6 ]+ c! A

$ I  a5 u3 K' {# G" `3 {. Y
1 H1 y" O/ A' `# e$ g# y+ [  H接下来切页:3 l/ k( ~; d: H  U/ z8 v' t3 H
先打开十六进制编辑器:+ d7 s3 Y' s$ B+ A. W! M2 F8 u: v

+ h1 C! u" g$ e: w1 M拉到滑块最后,看看重启中断
9 }+ K( q4 X/ _* b) I, u中断地址         中断         优先权 : c! [& @1 F3 S
$FFFA         NMI         中 % Q5 i* l2 U' e; {
$FFFC         RESET         高   j' d. f/ o0 t) p% [
$FFFE         IRQ/BRK         低
/ K) K2 k/ U; T7 H* z0 _9 ]  t  t# D4 o, c1 S
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。' n. o- A! m& t. d* C$ H
由此可知双截龙2的RESET中断$FF65。& v  @- p4 X; g# _2 q
接下来添加$FF65的执行断点:
9 V3 F; l" B+ m8 z$ X打开调试器:" o: {* |3 W* U, A0 Z0 y. m3 k

$ z- o2 D4 r0 R. [添加$FF65的执行断点:
: Y6 R$ S& e6 Q
# r" ]+ x! W# {+ X# N
3 T4 L. x1 E$ w& a) _8 c8 \  x; o3 q! x
单击确定:" X" [# m' }* h" h: H* t

8 n% H/ S3 U- E) d4 l) U
) k# k$ I0 b; K; s8 I/ Q然后重启ROM:% i/ u/ r+ ?  x, |% {

4 E! I, C. ^& @2 u" m调试器此时弹出来:. w0 J8 o3 x/ o( p

: [1 A- ?, W/ e$ M3 O. P然后打开Hxd,写一段mapper 4的切换bank程序:$ _, Z, f, g2 A0 T; B
先看看mapper 4的说明文档:
9 {8 V& k. |( c: b, [Mapper 4
& g; N5 w9 l0 \% U- ^. f/ L  Z. a. A0 L
$8000:  模式号
' l7 \, U/ P# \8 g; h        位D0-D2:
! k3 q; i& d* m" t        0:选择2KB的VROM存储体映射到PPU的$0000
+ o4 [+ W, v- m1 Z9 Y        1:选择2KB的VROM存储体映射到PPU的$0800. d' z) s7 q2 b( M
        2:选择1KB的VROM存储体映射到PPU的$1000
2 u. r3 e: g+ }: j* }# D7 A        3:选择1KB的VROM存储体映射到PPU的$1400$ E! {0 K1 ]; M; o3 ]
        4:选择1KB的VROM存储体映射到PPU的$1800
: Q$ i( l2 l# t        5:选择1KB的VROM存储体映射到PPU的$1C00. \9 c4 j5 x6 ]% R! U
        6:选择8KB的ROM存储体映射到$8000
- T) ~0 j! N+ B        7:选择8KB的ROM存储体映射到$A000- j8 g- ^0 o- N8 J: c! y5 h, }
        位D6:
0 o( I7 w3 G( q$ g7 e6 L        0:允许擦写$8000和$A000( l' x+ a+ e5 V" w( Q$ E
        1:允许擦写$A000和$C000+ {+ G# D$ M+ U5 a
        位D7:
! V( n* f) A3 I! O: t# r        0:模式号D0-D2使用普通地址
8 L- w8 x3 Q0 [( v; h4 t        1:模式号D0-D2地址异或$1000
$ |0 i* p+ c) A9 m- C& r8 H
* V1 |: D+ f/ \6 H/ |" o6 L# P1 a; @$8001:  模式页面号$ u% F# F$ y: ^( |# n9 {! q7 i
        写入一个数(00-07),切换存储体到对应地址0 ]3 L: C& M; _- E, Y0 ]
" [) p$ f: p& q
$A000:  镜像选择7 x% d  S: }8 ?2 p5 m
        0:垂直镜像
. \" j) Q7 I( G9 P- a0 n, A5 Z0 s) p        1:水平镜像5 \" w' ^0 `3 w& g& K* ^( P
8 K' j) q6 @+ P% H9 l: b6 |
$A001:  SaveRAM 切换6 r, I2 n  W" V! v+ P! Q
        0:禁用$6000-$7FFF9 N: u9 G5 M9 \7 t8 {+ H/ e
        1:启用$6000-$7FFF
4 C: Z% V8 x& x( \& h4 ~
4 H9 _# k" p/ Y2 q$ X6 h! Z2 j% I$C000:  IRQ计数器
8 S) Y2 o* O7 Y+ Z* N        IRQ计数器的值存储在此处
+ _+ u) i" @+ @% f+ i, L
* `  ^% W+ E0 B0 W$C001:  IRQ暂存器( p5 P1 c& h! @, F" C
        IRQ暂存器的值存储在此处
2 M; A* f- m! c# N; S( _) b6 u# `8 ^. M4 w
$E000:  IRQ控制计数器0# W9 N/ d1 _' c5 a: r
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ
3 Q2 Q; a) F) ?, e9 m  Y# h8 D, {9 B6 u. `0 C3 x
$E001:  IRQ控制计数器1
( p& w2 [) o/ @5 ?1 e% U        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)% H/ N+ n$ ~  E0 e" B, g

6 E: w6 z% ?9 Y. }% C+ {9 P5 h* N那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
# K" k* p3 D$ j2 j3 O48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68, R" Q, g5 l/ N5 z: ?6 P8 `  m
PHA      累加器A入栈
# t3 L$ E5 w) G( z# D# R5 yLDA #$06    设置切bank地址为$8000-$9FFF
& [. d& e  v8 MSTA $8000
4 K* ?3 y) M6 S- ?+ k9 ~: M& j1 M1 ^LDA #$0E    将第0x0E号bank切到$8000-$9FFF
1 h" Z& e8 e$ g- n2 T3 VSTA $8001
( s: ~3 N6 e1 v* F. ~& |JSR $8000    跳转到子程序$8000
* j, f6 Q8 M' U5 v" S- R" p- ePLA     累加器A出栈
+ |4 ~3 y% U" ]# B0 ^; L# v- u" W
1 B% Z) ~/ J% M' h! q- c9 Q为何切换的bank号是0x0E呢?: P: r/ `4 A9 K7 G9 d2 ?
因为在扩容ROM后,文件PROM结构为:
3 ]9 h2 K! ?% N$ O/ ?6 m) l0 F! I5 P原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM1 m5 ^  s! x# v9 ]' Y- g- R

% Q& Y- u5 P% `4 w+ [; P; n8 BMapper 4切bank一次是切8KB,那么文件结构就是:
2 a8 l& s$ ?: k原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
, j( P4 g2 R. ]因此选择扩容的第一个空白bank就是0x0E号bank。
" ]7 S7 w$ i3 N  y2 y0 ^3 B3 y5 K4 ]8 o9 H: O( ]/ {8 u; y! s
2 J# }4 R! N* n! A4 z0 l2 y
然后去调试器找RESET中断中可以放下切页程序的地方:. b# l8 J: ^; }
首先长度要小于等于自己写的切页程序。
, H$ ?2 s0 s0 I& m) [; b可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
8 f0 Q  ?6 z1 E1 `
+ T/ z+ R5 V7 \  K7 u0 y, `* O" C8 o" J然后跳转到$FF7C:
7 |) N% b. Z5 w0 c  \" P; W# X1 c7 J  p5 S/ W
% S# x2 U2 k  u
! z: M4 d$ C  _0 w6 ~: `( w& w8 v
- ?$ h6 z+ u) V3 C( w  b
复制下可以被替换用于写切页的程序:4 N* x9 t( N. J! \
先选中,再复制:
) D; a# X0 Y0 ^; g3 |
( a) k& G! I* X
' r3 }. o- j, u% o& A在Hxd中新建一个文件,把复制的数据粘贴上去:
1 H& `! e: M6 T. `9 J$ W7 D! D2 \2 H2 w, E+ K* V' s" }
# B2 j: ^0 n& _1 P1 ~; P

) `& `+ [" p7 R- R4 [" Y6 L5 w3 x% L2 F" Y) @0 D. F% {, Y
然后回到十六进制模拟器:7 t4 [" @; [7 G- T& C* |5 Q
转到$FF7C对应的ROM地址:
9 k+ M  i) \5 h/ p* s5 T
- |6 ^% h% P+ h6 D0 \  m
/ d' a; N: j9 e! x: Y" Z然后复制Hxd的切页程序,粘贴到这里:
7 B2 U. m. y5 A* m" A* E7 u: Q5 T
5 _5 r+ N  V' q1 A0 {4 i3 O( \- P7 g0 h" Y9 O% t; D6 R# m
4 `5 h1 K  @1 n/ L9 i5 X9 k
没有覆盖的用EA覆盖:9 o5 s6 X: c( V: Q  R1 R

- W6 U# z+ Y6 r0 H打开调试器可以看到变化:7 Z% J6 i" m& l) {

2 X  G6 c: {8 a6 O0 V9 `( d然后添加$FF7C执行断点:
) t2 r  f& Z' [1 H7 |: A, k& z! l. B5 w% G
) U* n" p1 m( \& ^$ M$ M7 o" R7 l
! ?( A5 S4 Z$ p( n" x8 m/ k
单击运行:6 K# X1 X$ A4 ^* E
然后程序在$FF7C这里停下来了。
! N! X' j% ?) R" Q" H1 c0 c+ E# R* I0 `, r  X# m. N5 w4 |" U2 R
然后单击单步进入慢慢跟踪,直到跳转到$8000:
, B/ i: g" e" s9 A# b7 E& v7 @; s( `" A2 Q4 X
然后打开6502_Simulator:
6 Q2 N: Y% L2 W- i" G" P' Y2 |
3 M  X4 l  x, _6 [再打开我写的数据搬移程序:
2 n# y& t1 f1 E) v: {5 u; Z" M
& i8 P  M' E" F% \! r: c; r( @! A0 I" K6 O2 I4 P8 a. u4 H, b- X
然后修改对应的数据:: `- G2 l7 J, Z" Z  S
程序开始地址:修改比如$8100就修改为 .ORG $81008 e. I/ @9 k+ C5 ^0 y& G  a' f9 C) X
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70; V6 q" D+ X- Y/ J; w
从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82
# x$ @9 m# ^0 v想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
: Z6 R- ?) e" A3 ~! ?, K9 b5 Y也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。
" ]+ @3 p' r$ Z! }8 n) A如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
1 D8 s5 o9 R3 Z1 a9 j5 e9 F$ Q中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。) |- V3 {" S! E7 |1 |7 ^
后面的不用管。
; N* P; C% f# x1 Z- `  |& G; R, G8 j& y
8 w# _; J. ~4 n& ?& r1 H/ W设置完数据后单击编译:
8 u& m3 i, f; x0 N3 \6 Q( e& f& v) [
# B/ N2 u) O* N然后保存编译文件:6 v' P" h  `# P- |
4 j3 M) `1 U( I3 V
选择二进制方式保存:! ]; @& r; O) T! M1 a$ l: I$ L
& R& V" }/ e+ e# b
* h5 Q, X# Y; g% v8 b3 H
用Hxd打开保存的二进制文件:6 p6 I+ u8 W  p9 x1 L
8 a" D1 Z9 [0 c. k- h' s
& m# K$ V4 I: y, o
跳转到设置的程序开始$8100:
; c( {! a8 a* {: g2 Y6 \7 Y: }) q! R; d" F5 [

6 O/ s$ B4 e) R6 ^/ V' ]' s) W& D
/ M7 |* p" M' @. B/ m回到FCEUX,转到NES内存的$8100对应的ROM地址:) k7 W( y# u$ m" d
9 ?" e& g+ N- S2 \/ y0 q% E3 G
) r6 h! l* F) z* X* N8 H& H6 H

- Q& \+ W) `. d* M8 [; z" N
1 j1 K8 p* B; T8 a& ]1 ^+ F) g8 @) g; k) c: R2 q$ U
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:) v) U$ d; z- `4 p: g
5 M* R% ]  G8 T5 Z3 H) G0 b
9 B! H* g5 S+ E7 h

! X1 z/ z, ?- v* R然后转到NES内存的$8000对应的ROM地址:
0 c9 t* D7 ]: @* Q& Q& O9 R( s, M- G# e8 j' ^5 I. ]+ O% y
  M1 P6 G  k: |
% Y" I4 \- u) x$ b& g. d
( a0 u- a; M8 \) a7 ^' A
写上如下程序:3 \# R6 I+ S' a' Y- j
A9 80 8D 01 A0 20 00 81  
' B% B7 K5 J2 J" u3 vLDA #$80
  O- r4 i/ k1 v$ e( CSTA $A001 可写方式开启SRAM  X4 a% K& x' _* x! v
JSR $8100 跳转到子程序$8100
1 z" \2 R' J  ?" K/ e8 I' Y9 u3 Y+ T3 {0 ]
然后把Hxd里被覆盖的程序复制过来粘贴在后面:
6 k: A' \2 M/ D( l( J( H, l+ O2 I' L& H5 K, G
  M2 N, C" X" t# B; \+ ?
2 n: q4 X2 r, I3 ~8 }! a- \( _) T
末尾补上一个0x60:$ G! w- A. ?% o" F+ W2 I6 k
RTS 子程序返回
* l  _3 j* [6 E7 H' S3 A; B# f" u" z
然后单击运行,ROM音乐响起,正常运行:# M( |4 D. Q, X9 X, A. q9 n

' M0 n! o0 g/ ]5 M* V* ?% A1 M" L0 z4 b7 R: v/ A
然后转到NES地址$7000:/ i$ a  _* ^+ t. U

; T  N$ Y6 L4 U' `. ^/ n; f! H6 d# `! n7 @' \/ W  A2 A! z. q# B: i7 G2 f
. t- W7 ^$ x/ y
, c2 f( A0 O- @4 t

: G& m' w0 ?5 t/ S可以看到,$7000-7FFF都被复制了一片数据。7 D7 c% b' P+ D4 [; l4 z
测试没有问题,然后保存文件:
. d& s1 p, D# M! }% j! X5 W) V+ h9 j) d/ S4 P7 B; T( F
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
+ q' `* a$ n, G* J  E1 H后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。& a' O# B: W$ |* Q" N5 c7 C
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

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 14:45 , Processed in 1.080078 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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