EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 7 q' c/ s6 ^  o9 N
4 z' {; t2 `+ R% X
[FC][SRAM扩容教程(Mapper 4为例)]# K& O- z' x6 a8 T( w) p' f+ B: Q

+ Y8 c4 ^; e2 C时间:2017.4.28( Q8 J" W' t5 O+ [5 g) O( r1 @
作者:FlameCyclone
* ?5 t/ V: M6 x工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator
4 n4 r/ Y$ }6 j) \7 V- gROM:双截龙2(J).nes4 K1 ^2 ?, R+ q5 E' z
适用:没有使用SRAM的ROM) V4 e8 y3 v/ E3 `, l& Z- A- t

8 R% W$ o4 s) _* g; {: a& g首先用Hxd打开ROM:
  n1 m7 `8 c8 k8 c. J  V, e- h0 O2 O: n* I# j9 s
然后扩容:' ^; k# B0 r; M' s8 _

) G7 k4 @4 G; F# R  @, w& b$ y: Y8 @0 f5 }( }

9 Q7 q/ ^- D, R3 H
, k+ c9 `: X) |  h# P
; L4 w, i# w8 d- v! ]1 M- N( d$ L; }: I, M- M2 u, ^# q0 A- f3 O
, {4 O* G3 d% U( y8 N- E
& l( {# U2 P# |) o/ N  X$ f8 }, q& N

% u- C1 y& c, X  ?/ E! Y% v; Y先看看任天堂产品系统文件对NES文件的说明:
3 e! W8 [. F" U! gNES文件格式
$ Q" `) |' i' c0 a3 T. J9 k; V.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
9 p+ d- E6 s% }2 e. f0 Q( Q偏移         字节数         内容 9 b  @" B3 P/ t& |
0-3         4         字符串“NES^Z”用来识别.NES文件
$ ?0 t; E/ j) x+ |/ J7 m4         1         16kB ROM的数目
* m0 ]4 h: z# v5         1         8kB VROM的数目 0 T" W0 ^& X6 `; K* f" g
6         1         D0:1=垂直镜像,0=水平镜像
3 V% D; @! X5 J' U  z                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
' i" v' x- \1 [/ M( r( q& m                    D2:1=在$7000-$71FF有一个512字节的trainer
: e( X' D* D0 [9 Z8 U% o  B  u6 h                    D3:1=4屏幕VRAM布局 ) @) M2 L/ D6 L' W' t( \% A
                    D4-D7:ROM Mapper的低4位
* e, C0 y# F; r$ A7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
. u  w9 `6 F) B' S+ |                    D4-D7:ROM Mapper的高4位
& }2 v& h/ }/ e8-F         8         保留,必须是0
- J4 H9 K# i. K, o* T1 N; S16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
" m8 U$ |, F$ a-EOF         8KxN         VROM段, 升序排列 # P! u- T* e% D2 {9 e
! _) x* h+ w9 N( E+ `
然后知道这个ROM有0x08个PROM和0x10个VROM8 ^5 p, l! |. t" g$ B  W
接下来扩展PROM位0x10个:5 E4 K, R: a$ x- E6 q* I1 R
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):9 r' ~0 I5 q9 G9 L) n! D
. p4 X, Z6 Q+ ^% P- x/ G
由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
" v4 F' |" _  i最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。6 E: O8 w; u# u
于是可以得到双截龙2的是:* a) Q2 u: G1 d) L; _7 h  w/ w
(8-1)x 0x4000 + 0x10 = 1C010。0 T8 E4 d0 ?( v
然后跳转到1C010:
/ _3 r- X& a1 S- V5 C& K4 P" r
* f  |  i7 x5 N% e/ v( [+ C3 G7 _2 [8 g: S" b) _% R

" T  ?3 E) U2 q5 a! y然后插入0x20000字节的FF:
% |5 Y" i# A4 B& n# G3 A/ X
' X; a. m) t& ^4 j1 j  E3 E: [" n: l) b2 D! I

( T6 o3 n1 o7 \. O4 V. B然后保存:
  i0 [1 D! }/ A4 ^$ [$ t: p0 @/ u# w+ F: S
) K0 e. U) C. N
用FCEUX打开正常运行:
7 E, H/ D' i6 N  O# y3 d/ Y. o7 ]4 O0 ]# b' F
查看文件信息:
7 ~4 B6 g* W1 Y1 B* v6 B- E( b) n5 Z( g

0 w' W$ z) X$ W6 s- \" g接下来切页:
2 Z- P& G; G  f' x7 l' y4 \先打开十六进制编辑器:5 h/ P5 ^0 r% r% `: H
1 E9 o5 i/ L+ q0 T, F
拉到滑块最后,看看重启中断
, k& R- z" i, i, S中断地址         中断         优先权 ) U, V; s; ^: }# ~. X7 b
$FFFA         NMI         中 8 m+ }+ I. R" K- E
$FFFC         RESET         高 3 j* Z  U7 _& i* `! C
$FFFE         IRQ/BRK         低 , s, t  _( K+ k, Y
# X% z. l( i7 T
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。' q# }" {% K; J( d# w/ K! X! e
由此可知双截龙2的RESET中断$FF65。; G+ y8 M; @* i8 i
接下来添加$FF65的执行断点:
3 R: `8 R, N% K打开调试器:
5 z9 e9 V# z6 r; e1 K( c% m# u% Q- ~' H+ y. \! x
添加$FF65的执行断点:& d5 d: L9 P# c1 Q5 m. L. T7 r

  J3 t0 a" N2 V/ p$ U. E
) D+ F' o7 X/ m
* _7 i% ?! p; N. R9 D单击确定:6 s; |" o  O8 @( c- `, Y% C# W

+ ^( V+ v6 m% Z+ F  o) \& F$ C
! U$ o& `' r( u4 Q6 B; S+ M8 @) E然后重启ROM:7 f1 t0 g2 m* {- q3 ^: b  x: H2 r

1 D" d# ]+ Y% q- U3 A调试器此时弹出来:
: w$ K# P9 D6 N! g9 p- q0 c! G! m& _/ B
然后打开Hxd,写一段mapper 4的切换bank程序:: z7 N* p% a8 C$ S9 _
先看看mapper 4的说明文档:
- A8 {' @7 P" O1 T4 v6 c& L/ aMapper 44 T( P  s/ W  e+ G  E& M0 [

& X# h- {4 _8 Y0 d( W7 H$8000:  模式号
3 J9 Z  n- H4 k+ Z0 L        位D0-D2:& j+ p& [7 l- H" M" `, L$ N
        0:选择2KB的VROM存储体映射到PPU的$0000% E! D% B0 L5 n) Y; w+ ]' i
        1:选择2KB的VROM存储体映射到PPU的$0800
# C0 l- n  t! c4 O% Y  v. U        2:选择1KB的VROM存储体映射到PPU的$1000& l: P+ h* ?0 t3 r' I
        3:选择1KB的VROM存储体映射到PPU的$1400
% T4 E. r3 Z7 V$ F4 Q        4:选择1KB的VROM存储体映射到PPU的$18000 X! h! p9 @+ w* Z* @
        5:选择1KB的VROM存储体映射到PPU的$1C00
% D( U2 T' W0 @! o5 D  o' Q        6:选择8KB的ROM存储体映射到$8000
: u2 U4 D& k; P/ T# H- i- x( b6 r        7:选择8KB的ROM存储体映射到$A0002 E; ~6 S' J8 b3 `" d+ D
        位D6:
- V& I0 c" Q, O! ?        0:允许擦写$8000和$A000& e6 i: n) _) u- b& B* I# k
        1:允许擦写$A000和$C000% q) c9 f3 t# i4 g  S
        位D7:; r) D2 p; q( g$ X& _' Z. ?, W
        0:模式号D0-D2使用普通地址: z9 l& ]' H, t0 [9 y2 j
        1:模式号D0-D2地址异或$1000
& Z  d+ O* N- X& l4 Q
6 r2 ~: p4 C4 \$8001:  模式页面号& n. D& X) f0 q
        写入一个数(00-07),切换存储体到对应地址
1 N& ]! M, g; d+ b5 R' A! o" z
; {' h! ?* L! W9 d- y' [$A000:  镜像选择6 ~6 t2 N/ f! o, z; Y: P
        0:垂直镜像) }: c* j" O7 T4 q  i/ |* ?: D
        1:水平镜像
1 y, w0 ?! D0 B1 e  Z0 k
+ F4 p2 ~: ~5 B7 \7 H# D6 W$ T7 d$A001:  SaveRAM 切换
' {6 E& Z! s& k1 R        0:禁用$6000-$7FFF8 S* u( Z0 a; C) W3 ]
        1:启用$6000-$7FFF& }! h; l1 [3 B+ Y% k

8 Q, W2 x& N/ G9 _; k+ U$C000:  IRQ计数器$ ~; ~" _3 {) M
        IRQ计数器的值存储在此处0 e% R, G2 J9 W( y7 e

& A5 L) }1 V7 V$C001:  IRQ暂存器
# s( I8 a7 S' _8 l7 C: j        IRQ暂存器的值存储在此处
7 K, `& G" ?) C# Q0 k
' q6 }  x& H- l; D/ Z$E000:  IRQ控制计数器0
; u1 W4 c7 q( I; A( f        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ( H/ t- s  e1 r% O' K% X$ j
$ x" U  ^* q  S. P) y
$E001:  IRQ控制计数器1# }4 W# _! X+ u$ J5 n" h3 [+ \
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)7 F: i3 L, G8 A6 W& C* }
. E; N  X1 R, i/ s4 N5 q- k
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
( w) ~' m- N/ N. F# N+ |4 |' U48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68
" ^; N! o) G' W) n' SPHA      累加器A入栈0 R) b& B8 U8 T) b# |- Y7 o0 ]
LDA #$06    设置切bank地址为$8000-$9FFF
+ N: `4 b1 N! ^- T; V" ISTA $80000 q$ i* P. F3 [* e
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
! J4 r9 b1 L" M# [' ?* q! z4 OSTA $8001
6 M7 @" ~" m* C# c0 H6 \4 vJSR $8000    跳转到子程序$8000' Y* f0 q: g" @: X* t6 @
PLA     累加器A出栈
0 \; E6 b6 t, V9 d6 J3 Y  S: ~- W( `# ~
* Q8 D3 k' X+ e为何切换的bank号是0x0E呢?
# a3 b' W- r5 ?3 N' D因为在扩容ROM后,文件PROM结构为:1 j- ~& ?. B: p" K
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM2 H' K# U+ C' {) C: d( t: C' R

! i/ n% ~+ d; C+ b+ k+ tMapper 4切bank一次是切8KB,那么文件结构就是:
9 `- q/ ]: w  k原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)% w: M  ?6 t, J, O0 V: m+ q& W
因此选择扩容的第一个空白bank就是0x0E号bank。9 Q+ M  `7 A) [; ]9 [

* R2 r% t& s8 y0 k6 q1 s0 k) B
" G0 q- M: M. |0 h- ]" W* W0 p然后去调试器找RESET中断中可以放下切页程序的地方:
: e/ A  ^% x9 x2 v0 b! q& m首先长度要小于等于自己写的切页程序。/ z, \' G4 a' `) [8 r
可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
( \( M9 j/ @, V7 |% y5 E& N& K
3 N2 {$ D" L. ]  G/ ]+ ~# ?0 J# A然后跳转到$FF7C:* i. h8 {/ J3 r( F6 U
6 ^( z3 T2 |" q7 ?* E4 B8 U/ \# ~

- D/ u2 M3 e! X# E( r- {* j+ Z+ ^6 v
; i0 z0 V% k: t0 A% {; S
复制下可以被替换用于写切页的程序:
+ c  r! i7 M5 L2 Y# B先选中,再复制:
/ u7 z/ {7 N2 }, K/ h# q* Z3 X* \7 ^3 g8 B/ D& [
" f3 c( U6 }3 r+ C; D" X, G
在Hxd中新建一个文件,把复制的数据粘贴上去:
$ F6 X6 z/ `, s1 g. q4 x) i! ^2 r! [! P) U5 ~' k# {

) C, T  ~& h. p2 f- x( q3 q  ~, T
- B! b6 s5 d/ |) Y# m2 c$ V. m! u
然后回到十六进制模拟器:
0 P/ ^, X6 ?( V6 V转到$FF7C对应的ROM地址:9 o* y0 d( _, `) v" w

' ]- g" S) E! h/ h% A4 e6 H& Q( o
3 s) e, {1 \- {" {& B7 g然后复制Hxd的切页程序,粘贴到这里:2 |6 V% t. ?- q& e  I2 _
# k9 ?8 H" i- n! E

; `4 Q4 r- M9 q" n2 j7 K9 Q6 h6 L
& X/ w1 o! n+ ^$ B% Y* x4 F没有覆盖的用EA覆盖:) Z% s, k6 B2 p2 b& g2 L

7 d8 o2 U8 y! T& S7 H打开调试器可以看到变化:4 Y' m1 p1 P. B) l
( m: i4 ]" V' a1 D
然后添加$FF7C执行断点:
9 B2 U" J( T" s  \% s) y
% F" J9 X" W- Y( |
' X1 f- J9 A. p+ t. ]; b! Y$ u! r+ |! L/ J0 T+ Q4 d& b2 }
单击运行:
, S) {' p& r1 G$ x& Q) }然后程序在$FF7C这里停下来了。
% H/ q  ^* l' i6 F' ]! o& A; {7 |( m! u, b' Q4 H
然后单击单步进入慢慢跟踪,直到跳转到$8000:5 e; o/ M) _/ {

7 ~0 \/ F, v# i$ s7 z1 A' T  q* j然后打开6502_Simulator:
  J0 E7 u0 f2 C( m  \. Z+ |; B8 U0 i  f, ]7 V0 h: e- A3 c
再打开我写的数据搬移程序:4 H3 k/ k& g0 X* m: _
) {  l! p2 _4 a+ B
+ S- ^# E, y$ r% t
然后修改对应的数据:
2 f3 n! B  t: ], s% f. W" Z5 t* A/ v. I程序开始地址:修改比如$8100就修改为 .ORG $8100
5 ~9 v( {) w$ B( n7 b4 y9 p复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
$ @! h! u, ?- Z* W- g) _从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82  D( B  n% X, {$ A. X5 @
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91
+ i0 s: J% Z$ D% C$ }' E也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。! W3 E/ O9 C1 n( \: q, `
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。, ^' u1 G& y. _) Z$ O" s8 l
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。
; a9 B6 v3 p) ?* {后面的不用管。) S: x/ r5 C# a. J. k1 J0 y( }

7 D" x0 j& j0 D- X/ w设置完数据后单击编译:6 {+ p# ?; v( Q4 b  I
# a; ]$ `  w9 O+ i! g0 o# |
然后保存编译文件:
  T7 R6 ]" r. j6 W
% T% [# a8 ]% W3 x选择二进制方式保存:# o3 S3 c7 n, U; P" o8 a0 ~

  @/ W: s1 u/ K# [8 \
8 O+ C2 Q. k3 y; {) `+ v用Hxd打开保存的二进制文件:: w% ^& e/ h3 L% {1 C

+ F' B3 i3 Y$ S# V8 g! X, L
: P" b" @/ ]/ H$ ]5 u跳转到设置的程序开始$8100:
8 {/ Y- a; j" I; f, X: S6 e+ d  }7 x2 u" j& ^3 a
8 m9 _( i4 b9 k0 \5 j
) F# s( V. G3 G; \$ d, v& D& J
回到FCEUX,转到NES内存的$8100对应的ROM地址:: d% G0 k! z2 N$ z" y2 K& Y

) I% j9 u. x( D2 H8 r9 T
+ `- @, T5 U& T% {7 T, V; s6 M+ D0 A) x& I  I4 H. I

% z( t( y, p4 {' y& p$ ^0 G/ A+ e& n' w9 y( v
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:/ m2 n# o$ e- E7 L+ B1 p, k

7 f  D4 D( f  t/ d0 o4 b* `- z. q9 e! u& q* P6 p  p

" n; I, {" V& C+ m2 w+ t* x- v然后转到NES内存的$8000对应的ROM地址:
0 ]8 K' C: Y' n/ M, w
( R. k2 P& C! q  {: u5 m3 l9 `8 K) F4 w* q3 b9 `
* v) q! [1 T, f1 L, y9 D; H
& P9 H+ E7 T) g) S1 P( v" w% P
写上如下程序:" ]7 N- Z+ p3 @" j; ~" d6 G
A9 80 8D 01 A0 20 00 81  ' k- x2 V% v3 Y$ e1 a  `
LDA #$801 V7 m/ m) v/ W6 A
STA $A001 可写方式开启SRAM
/ Q% ~; S5 S- T1 QJSR $8100 跳转到子程序$8100  S+ ?0 }* T1 J9 D" R+ L+ w

" s/ J) R5 f7 h) p* h然后把Hxd里被覆盖的程序复制过来粘贴在后面:
7 H! Q5 x* ^- Q: r
9 a! X/ [/ y$ n% H$ W$ [( ?
1 j6 p8 ]3 `- R$ p+ Z0 z: n  h" ?. ?1 i" e4 I! J+ T
末尾补上一个0x60:
0 H) O+ _; M6 ]1 v+ c# ZRTS 子程序返回
- S! {7 z* ^; n- U+ }; Z3 L7 B! Y& G7 K0 ~/ A
然后单击运行,ROM音乐响起,正常运行:( V: s  q) Y1 I; P6 C. P# A. ?8 Q

1 J1 L. `# w- u) ^( @$ f" Z1 Z" `5 ?! f4 o5 x% \
然后转到NES地址$7000:
, m/ S, r4 c/ i0 d5 u) G
6 O; _) O8 c9 U: M" X) ~( Z7 F3 @# ]3 L$ X

: S9 Q/ u5 Z8 P8 o0 [) C/ k* }8 f% A2 ^9 X9 U0 A" B
! @" k- \6 r6 ~: \1 t4 V: K# {
可以看到,$7000-7FFF都被复制了一片数据。* k7 A: k$ ^; P- O$ B
测试没有问题,然后保存文件:0 u$ l4 l& u6 }; T

. V3 t6 r/ c, `  d5 d; A以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。& P5 M1 S4 \. D$ ]- n& `, ]
后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。- [' g; Y3 O3 @
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1184 天

[LV.10]以坛为家III

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

签到天数: 1777 天

[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-12-16 06:38 , Processed in 1.125000 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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