EMU618社区

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

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

[复制链接]

签到天数: 39 天

[LV.5]常住居民I

发表于 2017-4-29 00:16:31 | 显示全部楼层 |阅读模式
本帖最后由 yandagui 于 2017-4-29 10:04 编辑 7 s, z2 r% d8 H9 J

* p+ W" W3 l& R[FC][SRAM扩容教程(Mapper 4为例)]
9 ^" g- A! V8 y" O9 F) p1 ^5 D  D/ R4 \/ I1 T
时间:2017.4.286 l0 \2 p3 k! r2 n/ D/ E4 p% g! v0 l
作者:FlameCyclone
" V$ a6 i" x# |  [6 v' {工具:FCEUX 2.2.3,Hxd 1.7.7.0,6502_Simulator2 S; g! {4 d0 g6 k
ROM:双截龙2(J).nes
) o) \! Z. \  F适用:没有使用SRAM的ROM5 u2 C& Z/ U) ~4 K( e6 j, N

& ?/ |- `6 A; _首先用Hxd打开ROM:5 P" v; w) z% T8 K

* r  |0 P/ r; I+ U* C3 m然后扩容:% m4 i( b6 b# p. ?2 K: M  q# @7 d

9 B# F# ^' @7 }7 L
6 ?8 Z* m5 g/ W$ F& U: r' y% v2 j  ~( Q

8 w- a3 T. t& H/ m  V5 b% W* e2 b
# \9 ^, u- G: C0 H! X# g! R9 a7 O
; e, n, }$ s& a4 k3 o4 H
  Y0 k# _2 _* A
; C0 f! C! U8 U! P: j' q
先看看任天堂产品系统文件对NES文件的说明:
7 s, a/ {: H5 {9 INES文件格式
5 Y9 z0 S4 t' _" B0 Y# u1 [.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。
/ g& j8 F) D+ ]( w4 @- M偏移         字节数         内容
3 C% `% S3 m" j: J. g0-3         4         字符串“NES^Z”用来识别.NES文件
' f* z. a( V, Y/ u$ {, J4         1         16kB ROM的数目
! j6 }% F0 v! p, W: h7 w  F5         1         8kB VROM的数目
! |1 F/ @, U9 q6         1         D0:1=垂直镜像,0=水平镜像
0 M6 |2 |. ^+ A0 ]0 Z5 l; X                    D1:1=有电池记忆,SRAM地址$6000-$7FFF . r& \0 ?" h: I( _% \+ D5 \
                    D2:1=在$7000-$71FF有一个512字节的trainer
2 Y0 ?$ `$ N: x, Z5 y8 B                    D3:1=4屏幕VRAM布局 0 v0 i% v6 V, @
                    D4-D7:ROM Mapper的低4位 ( ]* c( |  h6 ?& ~# R) b4 g7 f
7         1         D0-D3:保留,必须是0(准备作为副Mapper号^_^)
' f2 a& l% g. ~# j* _) G                    D4-D7:ROM Mapper的高4位 4 {( v$ ~+ o  r  ]" g% v7 n
8-F         8         保留,必须是0
- ?) V1 C* P  H7 b16-         16KxM         ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前
9 u! X& e; ~4 E% ?! V8 `. y, ]/ V  }-EOF         8KxN         VROM段, 升序排列
0 i% o! Z8 S5 [& [. m) ?5 R
$ f" j" @' P, v/ f# D然后知道这个ROM有0x08个PROM和0x10个VROM
$ I( {" ^( o4 }4 M0 l( S) d接下来扩展PROM位0x10个:( F; c4 [2 {" m8 M: J
先把第0x04字节改为0x10,0x06字节的D1位置1(设置有SRAM):) ]8 v$ B6 U) R  u$ s
$ {$ f9 \# h3 D/ ^0 d
由于mapper4的ROM在模拟器载入时会把最后16KB载入到整个64KB内存的C000-FFFF,因此需要在最后的16KB PROM(0x4000)前添加8x16KB PROM(0x20000大小),然后跳转到最后一个PROM的头部:偏移计算:
) D5 T9 ^/ x; `( M, C! A; C最后一个PROM的头部 = (总PROM - 1) x 0x4000 + 文件头0x10字节。! \! o8 H" r+ p' S& C9 w
于是可以得到双截龙2的是:
8 y0 b3 p: A0 m" d) d4 [/ g(8-1)x 0x4000 + 0x10 = 1C010。
0 b9 G# a; ?/ C然后跳转到1C010:! [3 [/ T0 K2 W- ~, C
: T% U$ }1 b7 t2 X
' o. i. Q/ o! m0 }1 j
6 U( |& S# \! x+ i6 D0 I
然后插入0x20000字节的FF:
/ O6 s: l: x! n6 |8 L9 \# [4 k( p$ r- ?% Q7 A+ F9 J
- g. r! q5 H  p- c$ g# o/ }
$ r0 O+ e/ W8 M  m. P0 G( H9 P
然后保存:+ F# @' O5 S8 @

2 w  F$ y+ ^9 U. z4 T2 t
5 t# k, {+ \+ ?5 N% _0 D. L6 R用FCEUX打开正常运行:* {3 _; Z- P5 ?3 ?
! w" q* v& d% z4 b
查看文件信息:
5 u' \7 N4 ?% h' h- F' r, t
% I* U- V8 R/ D4 }8 V  z4 _! P+ V- v; k* e- k( R& b; {
接下来切页:- e1 q/ N  h% h% q
先打开十六进制编辑器:! d9 D3 j$ _# c- E& I3 n
( Y* p; e. e' a# n  l+ y
拉到滑块最后,看看重启中断
( k  y) S5 T* ^5 Y* i1 s中断地址         中断         优先权   k* z+ N0 F( K2 `4 i5 i) w
$FFFA         NMI         中 % O$ L: O) z% ~" K! R5 w' B" W
$FFFC         RESET         高
0 t. n" U: D7 D' k5 E7 G, f2 n' G$FFFE         IRQ/BRK         低 , r8 c' L8 I2 n  p7 L9 f2 n
2 C7 t, Q3 S# _  i+ y- f
RESET中断是ROM载入模拟器后最先开始运行的地方,只运行一次。% N5 G2 H# r, U/ K6 L# m
由此可知双截龙2的RESET中断$FF65。' {7 ]4 `& @. f# H; W; \, H% m
接下来添加$FF65的执行断点:' ^1 n( |" ^( h# D
打开调试器:
9 p9 o" @* y2 q  A8 C' R* m! ~$ u9 z( A& Z4 d8 o7 ]
添加$FF65的执行断点:
8 f8 u, V( _+ z" g5 w. A. ^4 u" K% P- A( w7 T& Q; M

$ E$ [) O: T9 L
' q" P- Z& W& o, y: A单击确定:
8 X/ E- @) b: T1 z
  _, N, k$ n; C. C9 X9 N: x) Q  Y% Y( y/ }1 s1 @  T  m
然后重启ROM:
( A0 ?3 V2 m3 r$ ]
( K' U) i- h' r& Y( m! m# K调试器此时弹出来:
: `; y! {* x8 F$ \+ I* r) L2 P& w5 y* K4 f' d. e. S1 T2 G; p
然后打开Hxd,写一段mapper 4的切换bank程序:
  G/ m5 i+ n, i' v2 ?" \# I+ Y+ E1 t先看看mapper 4的说明文档:9 y4 [& m4 h& d
Mapper 4
; Q' G" [$ ]  A* G0 K  P5 f1 Q6 V
% Q2 T" U% m0 w7 I- a6 O" I  b' d$8000:  模式号
, }: b9 h+ l. D+ y8 R        位D0-D2:) t+ _, H6 j' C! o/ w- P
        0:选择2KB的VROM存储体映射到PPU的$0000/ B! W% N' c2 o$ ?
        1:选择2KB的VROM存储体映射到PPU的$0800
2 Q: g! n$ b) U  ~" e- I        2:选择1KB的VROM存储体映射到PPU的$1000
& S. W0 u. a: s  i2 V& ]' b        3:选择1KB的VROM存储体映射到PPU的$1400+ K( P- l) b+ y  k7 v4 p
        4:选择1KB的VROM存储体映射到PPU的$1800- X8 d" t% \- h. j1 K5 M& W3 }7 w- b3 ]
        5:选择1KB的VROM存储体映射到PPU的$1C00
6 O! ~6 Z) a7 |" F" k& N7 Q, V        6:选择8KB的ROM存储体映射到$8000+ i: r& ]: X) ?7 `! Q' e/ I7 @
        7:选择8KB的ROM存储体映射到$A000: D8 {8 H7 m- C- s( B4 t( S3 l2 Z
        位D6:
+ w3 O! o4 a; I        0:允许擦写$8000和$A000+ I/ D' \/ Y% X2 s9 q; I3 f5 Y4 B
        1:允许擦写$A000和$C0006 X0 z( N$ L- h. M& e( {' K4 k
        位D7:
2 [9 }' f* @5 i4 H2 x! K0 n        0:模式号D0-D2使用普通地址
, p4 J) W& v$ y+ g        1:模式号D0-D2地址异或$1000
5 `0 m# L% N2 k0 ?" o" E* J- T6 w% R9 P/ a: f% c$ a
$8001:  模式页面号& z/ t8 k# e% T0 t4 r
        写入一个数(00-07),切换存储体到对应地址
  q1 b* Z, X2 z) x, V# P, C' d: f) n! {$ r- j
$A000:  镜像选择
- a( A. L% R' @0 m2 m        0:垂直镜像
4 p1 }1 C/ p7 n. j& d% A. ~, J        1:水平镜像7 h7 {( u  c+ Z" A9 g5 n
( t1 C7 X: G- O7 Z! `7 e* p
$A001:  SaveRAM 切换
! p# }6 u8 |) m! a; E% p1 X0 t        0:禁用$6000-$7FFF
& U' a9 f; l8 m1 d        1:启用$6000-$7FFF  O5 T/ w+ s9 }7 o& d: l6 B2 Q

1 w* W/ p7 H- s4 V% s: D9 t0 E$C000:  IRQ计数器
! G  f7 z- M4 m  ^        IRQ计数器的值存储在此处) o# V; g% c5 q+ Z
: x0 J' q0 T! f$ x( Y7 o) g/ {
$C001:  IRQ暂存器- D/ b" V+ D% Y3 P2 Z- D& a
        IRQ暂存器的值存储在此处+ y$ }% P7 V& I/ D7 p& ]! |0 {
( E% o( ^7 I4 H% _$ O4 z; O
$E000:  IRQ控制计数器06 U9 B1 a4 p- d* u6 J* T7 R6 @
        向这里写入任何数来关闭IRQ,并从暂存器中拷贝数据开始计数,进入IRQ. O$ b5 U. e0 s" J+ l$ W% [/ \% J" x
3 U1 Z" \" v9 k; f4 w5 g
$E001:  IRQ控制计数器1
: r$ {/ O5 b! w1 w0 {8 {        向这里写入任何数,允许IRQ(退出IRQ,允许下一个IRQ进来)
0 a5 z6 `, k! f: V( P; E0 {' F6 i4 ~& M- D7 z7 j
那么就写段切页到$8000-$9FFF,然后跳转到$8000的切bank程序:8 ^, ]9 c: o! t) D5 |# }' @
48 A9 06 8D 00 80 A9 0E 8D 01 80 20 00 80 68
( c/ T9 d6 z0 `, J( BPHA      累加器A入栈
, O3 v0 w& M" H- m* HLDA #$06    设置切bank地址为$8000-$9FFF
; M" n3 H3 I" Y7 j' A) D* ]9 DSTA $80007 p& M! ~% w* w/ {( x! H
LDA #$0E    将第0x0E号bank切到$8000-$9FFF
* @0 s  x4 O- [/ nSTA $8001
" K9 V: B. s0 B8 z8 l: GJSR $8000    跳转到子程序$8000
' Y1 w% e( b; f5 W3 ^PLA     累加器A出栈
2 E$ |3 A& x1 Y( H* N* A9 v5 u
. B4 ]! T* A5 m1 D: W, R3 C为何切换的bank号是0x0E呢?
2 L% i) {2 z8 O6 X因为在扩容ROM后,文件PROM结构为:) b8 H; R2 D; m% r2 k
原来的0x07个16KB PROM + 自己的0x08个 16KB PROM + 原来的最后0x01个16KB PROM
' |* J  [) o" y( v( f+ z0 {4 f0 m/ H* ~1 p! f
Mapper 4切bank一次是切8KB,那么文件结构就是:4 V. p9 [4 D8 Z' S
原来的0x0E个8KB PROM (00-0D) + 自己的0x10个8KB PROM (0E-1D) + 原来的最后0x01个8KB PROM (1E-1F)
/ o% T* D" r' v因此选择扩容的第一个空白bank就是0x0E号bank。8 r  o. j) u6 U: l: f
0 G) Y" X5 o6 D- [
" Y4 }4 d* W( F' Y* ^1 k
然后去调试器找RESET中断中可以放下切页程序的地方:
6 [) Y6 C) Q+ k  {首先长度要小于等于自己写的切页程序。
( f( h7 K0 l/ V: d4 m可以看到FF7C可以使用(一般找程序中已经禁用中断后的地方,也就是找使用了SEI指令后的地方). j& e) |; n4 Z) f, Q
8 K" K' l& ]4 N( n2 z
然后跳转到$FF7C:
" v' H+ E$ L9 g+ c% \; R; \$ k1 a6 ~- s' K0 I
6 R! F2 a7 O" y& p) C. J$ ~, ?

, b. O, y# E& w4 Z; \2 s
8 i$ W% F9 X: f, T8 A复制下可以被替换用于写切页的程序:
7 q& E% o6 z. y7 B4 h+ O7 p( Q% U先选中,再复制:8 O. D. m; y; I- [1 u$ l2 A. B8 g
: G, Z6 ~. u4 T) k9 s/ w: _0 b

$ i& d( R) i9 N% M3 ~在Hxd中新建一个文件,把复制的数据粘贴上去:
0 c7 x( R% q1 B. t: V) f8 \5 |
6 T; r. @" E( \+ r* O  |) t( [9 N9 I/ X! [7 |9 `8 T0 K9 @

) D7 i3 E( N' B6 w9 r, Q! o2 K1 T  i7 x5 @
然后回到十六进制模拟器:  h0 y% ^% E  y) M+ e+ n6 a9 T1 V
转到$FF7C对应的ROM地址:
) T: V8 c) R3 i: P; o) C1 e
- X4 p* j4 R2 m' H7 ?1 f# m- S6 P; T# P* h4 f! A1 I
然后复制Hxd的切页程序,粘贴到这里:4 u6 G; D' `7 M6 t7 m1 ^

  T# n' s2 T- z3 D+ W# l4 p" q
, ~/ L$ f3 O4 ?7 j
: F- k: r% g7 \& P% Q( j没有覆盖的用EA覆盖:
5 G2 N' J3 v8 c& g
8 E+ N9 y- k2 z  n6 M! \! j打开调试器可以看到变化:
1 x! F3 u! `. Y/ j/ r. T
: E8 G4 M9 e% t. ~# B/ V9 u然后添加$FF7C执行断点:, P, E; i2 F6 Q# E, |
4 U3 `+ I# `( g0 O! G6 i

) {# d* i: G+ X6 g" P  s- `6 n5 E" p8 j" y( m7 I
单击运行:& \3 A9 W4 H4 ], ^. b& ^. ^- p* S1 Q# F
然后程序在$FF7C这里停下来了。* _' [- b/ X9 v% [+ h  M- ]% L

- i; `  v* G4 u" U4 P1 ?7 }然后单击单步进入慢慢跟踪,直到跳转到$8000:1 z; j+ ]% u/ H9 {, D1 J* G" C) }* y7 ]
' B# N% P6 j2 u- h: q. j
然后打开6502_Simulator:
; p! V; H8 E. m2 f: {. `( Q, _/ F, r+ T5 w! Y
再打开我写的数据搬移程序:- d4 Q& l) \+ n0 A  D/ T+ x: K* {
: u2 S: z; p& v9 y, A1 H! q- R8 ?+ n

: U/ y) N% A( ^) m! X2 [然后修改对应的数据:% V8 Q4 W, d/ U& H4 J2 N3 E( j
程序开始地址:修改比如$8100就修改为 .ORG $8100; E" c" r+ Z5 q, `
复制到什么地方就修改Addr_To,比如复制到$7000: .BYTE $00,$70
; ?; o4 i) d4 e7 x" ^从哪里开始复制就修改Addr_Begin,比如从$8200开始复制: .BYTE $00,$82* f5 {& ~& R7 i/ Z' R
想到哪里结束复制就修改Addr_End,比如复制的数据源地址到$91FF: .BYTE $FF,$91' w" S' ]# j0 c. J
也可以直接修改为 .BYTE $FF,$FF(复制目的地址到7FFF时会结束复制的)。7 t3 s( k  a5 @0 L, P' H. V
如果复制的数据最终超过7FFF,那么程序会结束复制,7FFF后面是程序块,不能乱搞。
9 f. X  g" K9 \9 @中断地址可以不管,因为在RESET中中断已经禁用了,程序不会被中断。2 V6 E; ?8 g# L& f% L
后面的不用管。
, p, E, C4 ?" {1 i5 p: A6 N* N8 b9 {6 v2 j* h- e- c4 K; d7 C
设置完数据后单击编译:1 \* I7 }/ ~; n+ k

; w' P8 O9 \& S3 t然后保存编译文件:
- {2 m- M1 F8 B5 @+ \( I$ o2 f" t5 t9 c6 v4 n
选择二进制方式保存:3 x: x$ N& y$ B" S% w* d/ e4 _
- S0 D% ?2 b) `5 K1 h
5 O3 [7 W6 w8 T+ S+ V
用Hxd打开保存的二进制文件:# p, ?& ]  H9 q, h# A

, O2 p% C+ O0 B+ U; |( r1 t7 [0 g4 x% H: H4 }/ L
跳转到设置的程序开始$8100:* o* b) \3 H, X+ p' h0 X  J
! \% a& @6 Y8 t5 C5 M1 U4 K; F3 b

& T) q: M: l4 s6 ?, d# g' @2 v3 Q% D7 Y7 B% V
回到FCEUX,转到NES内存的$8100对应的ROM地址:% c$ ~8 c/ R# |2 J9 k6 B
1 i7 T9 |5 U6 F- L% r! b+ ~3 ~
  p' p2 F( I7 E' M9 w2 F3 V+ y

3 X8 q; X$ {* m5 s: l
! b( z0 I( p8 N+ M
/ U: k+ G: v. Q) R/ m* X然后把Hxd里编译的数据搬移程序复制后粘贴到ROM里:* _8 B1 \- Y8 F) A9 I$ k9 Y% a
' x* B, ^9 ~1 B7 m1 h/ F1 F% M# p9 f9 C

" R6 y9 L5 J$ }# x! m' Q& T) |% X4 p2 u% N5 D
然后转到NES内存的$8000对应的ROM地址:0 J: t1 o8 F( e3 w
5 b( t* j% j3 m) p" K9 H$ B/ z
9 X% ?3 _# `0 E: C( f% I0 K

: f6 _6 t& {8 K) s5 |' W
8 l% R2 s% J, z9 d4 {写上如下程序:
- l6 F& E. h4 {. V2 I& {+ wA9 80 8D 01 A0 20 00 81  ; e' _9 F( r9 h& a) w$ v
LDA #$801 o$ H! s0 b- b( }
STA $A001 可写方式开启SRAM
) u" M; A) }7 `( V/ ]JSR $8100 跳转到子程序$81002 P( ]8 {9 q6 l8 w$ W* w

/ f- |; p! s* U  |2 A. k: m# o& t6 m: W然后把Hxd里被覆盖的程序复制过来粘贴在后面:" I+ B' j9 F/ e
  V' P% O4 v/ c! Z
& G! w0 O5 v, D

# J/ v, [0 E' ~& T6 t末尾补上一个0x60:
, I; ]4 |9 \/ V3 b( E3 j4 HRTS 子程序返回: V1 P& R' M7 N# b; I

' h  J% }# M; `+ I% T! h8 ]然后单击运行,ROM音乐响起,正常运行:' s+ s* a* `* [

; a# l1 s4 Q) g& x  k
& P2 B* l8 }' X% ~: s' Z然后转到NES地址$7000:' }! Q) c$ u# h4 C$ E7 a
4 ]8 [' f0 i" T0 H6 C, h

& B* y. F. d9 V( H4 l2 f. P: T0 C( K& ?4 ?5 \

7 h" }  B" \/ K$ W
; C- M% B4 f6 S, a+ i9 H1 J% a可以看到,$7000-7FFF都被复制了一片数据。# C& f$ S! D, b4 r
测试没有问题,然后保存文件:% o; {! Y4 H( a* l( W

2 T( P% g, S7 }& a/ v以上就是Mapper 4 的复制数据到SRAM($6000-$7FFF)教程的全部讲解。
( T4 a- c: \( o( c# a8 b" {后期修改ROM时遇到没有空间写程序时可以使用此方法进行扩容,将自己的其他程序放在 复制开始地址 至 复制结束地址 之间,这样ROM载入模拟器后会执行一次数据复制程序,把自己的程序复制到$6000-7FFF之间,后面的修改只需要跳转到$6000-$7FFF之间自己写的程序那儿就可以了。
3 j' J8 m9 \- f( E4 d2 m
游客,如果您要查看本帖隐藏内容请回复

本帖子中包含更多资源

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

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

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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