EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑
% L- f* {4 Z' A5 {: K  D2 U' m! |: Z' o, d3 [- ^) ^$ \5 A
[FC][SRAM扩容教程(Mapper 4为例)]
* Y% k+ P8 n) |$ w7 q6 {6 c! k+ O# ]$ S& x  m1 z# h
时间:2017.4.28
* j7 m6 ~% j" Q' {6 ?+ m7 ]/ q作者:FlameCyclone
# h* N  G+ M6 S% l& K工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator9 R$ l; ~: l: u4 Z# D* D3 N
ROM:双截龙2(J).nes
# m7 e2 J% L; r5 e4 t' T. D: G7 ~3 K1 c! f适用:没有使用SRAM的ROM5 ], e7 @( N7 w9 z# |
# N* l5 e+ e$ @4 c+ O& K7 d
首先用Hxd打开ROM:9 i$ c3 r: d6 q9 I1 q# {3 Z
0 [# }# u& @- J6 \
然后扩容:  E6 t! e) X! Q  a1 p% D
4 S/ q. M2 ~. ~9 g/ K+ o

' T. Z) X8 J* ], i
7 [5 |( }  L/ `. k! T& L1 K+ B; K* r3 ?6 v, o+ Y" }$ T
. P- ~$ `* U( @3 R3 e

7 u4 e8 l% y  C! v
# C- R+ Z& t! [' s6 I, B9 C6 B
0 s+ |2 w+ b$ Q; K* F/ T
( \  S0 Q0 Y& k& x' H先看看任天堂产品系统文件对NES文件的说明:
+ C3 c' v! y' Y5 H  rNES文件格式
' N3 [, s' \7 u4 B$ R3 s" J1 {/ c.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
5 w4 [8 D4 l& X) t偏移         字节数         内容 / \; r7 O4 \4 G. u4 b5 W
0-3         4         字符串“NES^Z”用来识别.NES文件
! |5 x3 a3 a: S& m5 S# }4         1         16kB ROM的数目
1 Z2 ~$ o( i7 }5 ]% b5         1         8kB VROM的数目 / X* i# _: v- I# k- Z
6         1         D0:1=垂直镜像,0=水平镜像
* P9 S" ]' P; I1 h& D+ F! c                    D1:1=有电池记忆,SRAM地址$6000-$7FFF
% l! z$ r& W' V                    D2:1=在$7000-$71FF有一个512字节的trainer   i  m" _. f; C' V! p
                    D3:1=4屏幕VRAM布局 # X+ Q& n+ ?. a) r% [% f
                    D4-D7:ROM Mapper的低4位 , ^8 P2 A, B& k2 e! h# g! K
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
1 Y9 H2 R+ t9 `' T7 i/ B2 H                    D4-D7:ROM Mapper的高4位 3 L7 V4 B. E. }1 D
8-F         8         保留,必须是0
; Y7 `* `; h- w, k16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前 6 O4 u' r; ~9 R) z+ B. G8 w4 S
-EOF         8KxN         VROM段, 升序排列
4 X$ p+ I5 z: S1 b
- S" t, O8 {! ^' \然后知道这个ROM有0x08个PROM和0x10个VROM" ~" I1 L" g  T' d0 ~6 J
接下来扩展PROM位0x10个:
+ A3 \  [$ E$ e+ N1 {$ z先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):! X6 L' X& s: p2 N. j& Z
6 I6 l, M5 f7 B
由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
% u3 y" V7 i4 I, L& Q" c1 x0 K' u$ J最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。( c& G9 k, d& ~
于是可以得到双截龙2的是:- F! h: }* K/ c& ~
(8-1)x 0x4000 + 0x10 = 1C010。' e4 O* P: O$ z% f
然后跳转到1C010:
; A9 [/ |9 J$ d+ i* a  a" z1 b5 E; J7 r, D0 D9 Q2 {

6 X* x. s) @- y: i& C0 l2 V+ @  W1 z' ?, E# I
然后插入0x20000字节的FF:4 P! Y0 k. S- r  O+ E- Q
/ Q/ D3 [5 l3 [; p" y! A- r# t, @

/ z; q8 U; G+ r" b
$ H! T" P* S) z: x$ ^# L2 D; r( ~( y$ l然后保存:
. `1 P9 t1 U% Y( d# |
# |1 o, L* \9 b& E0 [4 q3 D' \, N6 P! w, G
用FCEUX打开正常运行:
5 ^/ A, X" k  C3 ?6 C
; i7 t9 q; [& a查看文件信息:! c4 _: r% a9 w# X/ I8 ^9 W2 Z
) a- P9 t8 [" O- b) u3 Z) E

8 a! A, o5 T( y. s" s  D接下来切页:
5 w7 \2 E" b" m4 t$ t7 ]. B先打开十六进制编辑器:3 Y; Z) _, b% i+ _8 A

5 P" c: j" H' X& |' J- E拉到滑块最后,看看重启中断7 l% }$ o) C# |8 }2 m. j) B/ J
中断地址         中断         优先权
7 W! V) R9 G. j  j$FFFA         NMI         中
3 A) }1 |9 i4 ]& P9 @$FFFC         RESET         高 " W8 k: I: U, M* X' K- h" a- a, }
$FFFE         IRQ/BRK         低
2 t& R' S4 b" F& W- N+ g. {) S- `
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。% y/ ~* A) x1 `$ c2 R6 F* Q
由此可知双截龙2的RESET中断$FF65。, p: u9 ~' t, V  O
接下来添加$FF65的执行断点:+ L. `; W" O" `5 V/ m+ h, A( c
打开调试器:- f; P2 T# f8 J+ T

% v) h3 G4 M6 C9 J" V6 m; H( V7 m# l添加$FF65的执行断点:
7 y7 Y8 p. A6 I4 C. g; [% h# h' s$ j6 F. D  p  @" y

* @1 S) h2 h6 ~  `: m1 B; r- D$ ]1 @8 s0 a" S
单击确定:$ w5 Z# I4 Y, V) |
% G4 c7 M7 ?0 P( Y; j. C

/ ^+ ^3 T/ M/ F+ T) G, l然后重启ROM:
% n/ G$ l5 q/ C% o4 A: [8 k" s+ S: z( a( g/ ^# I
调试器此时弹出来:5 }, v* P/ ^6 j& r& f5 @
5 q( U6 w" L3 r9 a3 ^, J. h& ?* t
然后打开Hxd,写一段mapper 4的切换bank程序:
' Y, j5 O' X- }' e先看看mapper 4的说明文档:& }! i  v, F; Q  b" l. J4 Q
Mapper 4! ?! P( F5 c- ]6 [5 w5 w, n( h! b/ x
* B3 O* R. g7 u( E3 l* H
$8000:  模式号
0 b7 x5 V+ S# s; u- d4 i- G# f# s        位D0-D2:# ?& M! Y* Z2 H
        0:选择2KB的VROM存储体映射到PPU的$0000; s; F; s$ ^( A4 W! |7 D! l' S+ H) i
        1:选择2KB的VROM存储体映射到PPU的$0800) U, h. y; S, p: u
        2:选择1KB的VROM存储体映射到PPU的$1000
: O' s' |( B7 u: {        3:选择1KB的VROM存储体映射到PPU的$1400
8 y! @8 c) C+ E8 m2 A/ M& H        4:选择1KB的VROM存储体映射到PPU的$1800! n- z$ E% D1 {
        5:选择1KB的VROM存储体映射到PPU的$1C00$ J2 j- `' ?" _1 a) @0 [
        6:选择8KB的ROM存储体映射到$8000
9 g( s; n2 U! D& g        7:选择8KB的ROM存储体映射到$A000+ S4 w! z# M2 U8 A9 m, f
        位D6:, e' G8 `5 a- a6 w3 v( H$ G
        0:允许擦写$8000和$A000- {5 N. |& S5 O0 O( L
        1:允许擦写$A000和$C000
+ T- Q- L+ w% J8 u" g0 ~8 ^' H8 _        位D7:' X9 G* j* Q# W8 m1 Y& z( }
        0:模式号D0-D2使用普通地址. Q! `- i  N' [' C" v0 r
        1:模式号D0-D2地址异或$1000  f! I& Z9 t1 X5 h! `9 u" q: o
- Q  h* C" Q0 ]) C0 M
$8001:  模式页面号
) B  |8 N7 s$ p        写入一个数(00-07),切换存储体到对应地址2 y9 ?! T( k) e3 A
6 G' P6 y, b2 O
$A000:  镜像选择6 b/ j/ }' k. V
        0:垂直镜像
8 j# p: |$ M5 b: }2 B        1:水平镜像0 ^- p: Z# V9 r+ K! U3 P
' J% Y/ e* p  O& {. E( D' a
$A001:  SaveRAM 切换
# }1 {1 J2 w+ l6 n        0:禁用$6000-$7FFF) T0 w! Y. a% `7 j  M8 i3 E
        1:启用$6000-$7FFF& T1 Z+ ?8 j7 f! E  o

5 ]6 r1 i) l* t- o% J  K$C000:  IRQ计数器
/ b7 t. i0 y  `- D: R1 }        IRQ计数器的值存储在此处9 @5 E. n& S& m& a

/ a  e! ~0 e0 `3 Q7 v4 \$C001:  IRQ暂存器
0 i: _- M% H' P, [, A        IRQ暂存器的值存储在此处/ t( k( {& X9 d% N, g
  m/ |8 {; m+ o0 r: q' l8 z% S
$E000:  IRQ控制计数器0$ u. f8 g+ I$ d6 k& v; f& Z
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ5 |1 k; J7 T8 o: _% k1 V, {; B2 @  H

! N: z9 Q1 J' C$E001:  IRQ控制计数器16 a% H$ z; \4 a4 Q. W
        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)- P& z. m, n" S5 |

" S3 g  K& b1 @9 H* j那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:
" H* j! V& g, }8 L4 L" w" X, y! A2 P48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 681 k4 r5 _+ l, A2 e$ t7 l
PHA      累加器A入栈; I. V( u0 a" `. e$ `- l
LDA #$06    设置切bank地址为$8000-$9FFF
# {3 \2 f: I' F, pSTA $8000$ w. b! D) A$ f( ~
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
6 N- U4 r/ h, B: KSTA $8001, q" B' e8 @- ~4 K  M6 r3 {
JSR $8000    跳转到子程序$8000! ~4 C9 T8 Q% `( n1 J( j
PLA     累加器A出栈
" V! e# j7 ]( n, L" ]6 X) s
4 f: A1 B7 i$ r2 O$ q  A为何切换的bank号是0x0E呢?
& O, M0 O* a" e% [" O因为在扩容ROM后,文件PROM结构为:
6 z. U( C4 \& [" v1 S- l2 p原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM' L8 O5 ^0 u" h* u( P2 p' a( K
! h$ e: n4 I  x) J
Mapper 4切bank一次是切8KB,那么文件结构就是:
/ S# R8 n4 F( N+ u2 j, i3 B原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
% Y5 S. n3 I3 T因此选择扩容的第一个空白bank就是0x0E号bank。
* u# {6 n: r+ ~0 a* {( P2 P
7 v3 I+ W8 I) K2 R$ o. K" X7 }$ e  \# b7 `7 h) r' Q( `; Z! g
然后去调试器找RESET中断中可以放下切页程序的地方:
/ y/ M6 ?' ]! \/ r' M5 O首先长度要小于等于自己写的切页程序。
6 E+ Q" E3 w8 E: \% c可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方)
- w  N" W  w; o) a. a+ S) E0 v* Q* v1 L5 X$ L1 \
然后跳转到$FF7C:
$ b6 S' i$ P* L9 e) E; _) M
) ?# q0 H  O% e. ?2 ^$ q% H8 k/ {0 }2 `

- J3 D& `6 V4 U. L" N4 @4 o- a) X; N6 ]. d& i- \4 `  d
复制下可以被替换用于写切页的程序:
9 I$ t/ A( S  [7 W先选中,再复制:- B7 p, }  Y9 r! f; q+ G# z

( A, K# r3 B- }# ], |; ~# L& l5 Z& z* l6 \: R$ c1 i  |
在Hxd中新建一个文件,把复制的数据粘贴上去:) M2 {) f: H" b* ^
- r$ p2 f5 v) K+ D. u

2 q2 h! d+ ^2 }- v
! g# w# d: }- |" e
4 Q( S: A0 z2 i/ ~# `然后回到十六进制模拟器:2 ]" Q: [* ?1 U( Z5 `
转到$FF7C对应的ROM地址:. Y( F5 w7 w& u% @* |  X1 c

9 A& M6 b. c7 B! z7 ]$ f
( T! R1 T8 j; B" m. ~- F然后复制Hxd的切页程序,粘贴到这里:
* x% S5 e5 _: m, l2 l& o% L! d# y1 }0 f. N+ Y; k
( u0 S+ @/ A1 n# V5 J) D
. Y, J" z4 K6 z3 D$ N; F8 {/ o) X
没有覆盖的用EA覆盖:
' O9 p- u- {2 d( z; W2 Y0 I4 f+ _
& n- D5 |1 e# a5 k% j$ s4 [/ g打开调试器可以看到变化:
( y  h  e- p& O5 X* Z$ l: c0 p8 {- V& _! i
然后添加$FF7C执行断点:
; x3 e& q% {3 ]/ ]! B" }$ k# x" O7 C* p' _

2 t% Y' t3 ^6 y- X( a
3 T1 M$ Z9 u4 ]单击运行:$ g7 H1 x9 V# }
然后程序在$FF7C这里停下来了。+ I& I5 Y0 ?2 ]( }" k
- ]& K2 k5 Q, G% n6 j9 U1 _# f& D
然后单击单步进入慢慢跟踪,直到跳转到$8000:3 l/ ?2 G, X% Z- Z5 M  h

% }# o5 y& l( z, m: O然后打开6502_Simulator:
" q* m. R* g" Y7 K/ W/ O9 h* g2 R$ {9 g& z: K# ^
再打开我写的数据搬移程序:3 i" m: F1 I& H) _

9 K0 H/ E: c8 @1 [; k" U4 @0 |2 o0 i! D7 Q! O9 R/ T
然后修改对应的数据:6 B' ]9 e! K' r7 e; X0 j3 k
程序开始地址:修改比如$8100就修改为 .ORG $8100
9 b' W' C8 X2 V. B1 F复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
+ K% W( R8 [+ h; v从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82
. R, s- @% w4 W0 @( p& i' X+ L想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91# D+ ?* @5 U( M! K' {& ?- G7 `
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。; f" b* L4 B1 [7 ]0 M4 l* w
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。8 j/ d# ^9 z* s: C5 i; V
中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。0 {9 r! N" k6 g3 m2 l. h- ]
后面的不用管。& [3 @( ^0 B( Y/ Q( H( M  s
, r4 s2 T$ |# C7 j; a  D
设置完数据后单击编译:
, B. ~# H& ?! u  A( \
* a: x: ?: F0 k( M& X' A1 W然后保存编译文件:  d# k7 P" V2 i  @% }1 s
8 z9 B1 V1 ]4 b1 H# b8 K
选择二进制方式保存:
1 g: _- p2 i3 [
9 r7 d0 `: ?. x: v4 k/ f! @' i; r) r/ |
8 }7 J# h8 W0 S" p8 X9 e用Hxd打开保存的二进制文件:$ Q+ @0 k/ k% T7 O+ U, W4 }

7 q9 m1 |, N  T0 {- X7 p2 ~( \8 X3 q& C7 N& I- |; g
跳转到设置的程序开始$8100:. k: l* W7 M+ b  j  O3 n* C9 T& S
2 Z6 S# C3 D# r& l8 K* t
" U' r, Q# L' r" D5 d& c# V8 r
+ J- s8 U! U* Q# e9 D
回到FCEUX,转到NES内存的$8100对应的ROM地址:, b7 `2 k8 f0 e3 e" {5 C

/ l5 B; z# h7 O7 Z8 i1 G" y- Y
3 @$ L7 _+ o* v8 O" r5 s0 j" M6 X1 p- S5 K
& A; M( P# c9 t4 u9 e$ x: Y) `# N
; ]  R; M4 O4 m7 F3 m
然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:
& x5 \% X1 Z/ Q+ i3 j* B9 m# h  i- x7 x: ~& [. ]
0 j# I5 \$ y2 H! x) d* T3 D6 r/ K

4 H% C* L8 b8 z; W然后转到NES内存的$8000对应的ROM地址:
0 I8 v! j% v2 [8 f) Q: I
4 X% _7 P% V4 L% [" c/ m6 Y6 h; u7 `9 ]8 x# y; O

0 p# B7 D; R3 g7 T- M
- s( m2 p5 Y& b, r: f# W8 d写上如下程序:; h  A) R, O& V' \
A9 80 8D 01 A0 20 00 81  
2 V+ i8 j# v3 U$ x0 t  U% _LDA #$80
) B* ^# k# g, cSTA $A001 可写方式开启SRAM7 O$ D1 Q  z1 @- I. }
JSR $8100 跳转到子程序$8100* i: R+ \0 Z4 y% b' j. y
) ~6 z% X5 t) h9 p5 \
然后把Hxd里被覆盖的程序复制过来粘贴在后面:
. w0 {9 D* P- \1 y% y
* l& K5 G- X4 O. p) U, p& v  ^
; F/ s( j$ o% F3 }8 Y
% b, A9 k& Q7 Z( g末尾补上一个0x60:
$ H6 `6 V2 v5 G9 f2 cRTS 子程序返回
/ U9 s+ |: R3 v3 U2 `+ b2 B1 ]9 d( f' [# H
然后单击运行,ROM音乐响起,正常运行:! s8 m( C) O- D. ?; y
  n, u6 {1 e. f& E! n) e4 }5 E

5 D: C3 S9 D/ |" G& s然后转到NES地址$7000:3 Z6 b- [; Q' t

3 j; s3 a1 I* l0 ^8 F/ |1 m7 `
: _+ i- k; d1 g" x: L$ J& \& a& }

7 f3 v1 C) d( A* e
- a% i9 S6 g( H9 g, E8 Q可以看到,$7000-7FFF都被复制了一片数据。$ D* L' G2 X9 Z  {7 w
测试没有问题,然后保存文件:
: c- c! z2 t8 c3 T7 r8 \# t  s/ L$ e6 P, N' Z
以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
! f8 X' a% m- @+ U( G后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
4 h# `9 m1 Y: _
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

x

签到天数: 2060 天

[LV.Master]伴坛终老

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

签到天数: 1184 天

[LV.10]以坛为家III

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

签到天数: 1751 天

[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-18 17:36 , Processed in 1.124023 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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