【原创】NES第十波:解说一个NES音乐贺卡的源代码
我将自己写的一编音乐贺卡源代码拿来做解说。完整的工程和源代码见最后面的下载链接。
之前的解说都是保姆级的。从这一章开始,就变得简洁了。
一、定义内存变量(汇编都是用全局静态变量的)
COUNTER = $00 MUSIC_OFFSET1 = $01 MUSIC_OFFSET2 = $02 vNamL = $03 vNamH = $04
我只用了5个字节,好省。
二、初始化
;.start reset
.org $C000
reset:
sei ; 禁用中断
cld
ldx #$ff ; 初始化栈顶指针到$FF
txs
inx ; x=0了
;stx COUNTER
;stx MUSIC_OFFSET1
;stx MUSIC_OFFSET2
_loop_1: ; 清理内存
STA $00,x
STA $0100,x
STA $0200,x
STA $0300,x
STA $0400,x
STA $0500,x
STA $0600,x
STA $0700,x
INX
BNE _loop_1
sei 是禁止IRQ中断,本代码也用不着,而且初始化时,能禁则禁,后面使用时再解禁。
cld 是关闭十进制运算。正统的6502有十进制运算功能,但NES(或FC)的cpu不兼容十进制运算功能,或者说没有这项功能,所以必须关闭,防止cpu出错。
接下来初始化栈道和内存。
三、ppu热机
; 原写法(热机)有点区别
_vb1:
BIT $2002
BPL _vb1
_vb2:
BIT $2002
BPL _vb2
ppu上电比cpu慢,就是说通电了,cpu能快速启动工作,但ppu还没有达到正常工作的状态,要等一等,大约等ppu发来两次帧信号就可以了。
四、ppu操作之关屏和配色
lda #$00 ; 关屏
sta $2001
LDA #$3F ; 写入配色盘
STA $2006
LDA #$00
STA $2006
LDX #$00
_loop_pal:
LDA bg_pal,x
STA $2007
INX
CPX #$10
BNE _loop_pal
现在才可以对ppu操作,初上电时ppu的内存是随机的,那显示出的画面也是乱的,所以先关屏,不让玩家看见。
下一步是写上配色,就是写入调色板。调色板数据,已放在bg_pal这个地址,在代码的后面。这里先向端口$2006写入$3F00,再用循环连续写入$10个数据。
五、ppu操作之清除背景
LDA #$20 ; 清除背景 STA $2006 LDA #$00 STA $2006 LDY #$04 _loop_ppu_1: LDX #$00 LDA #$00 ; 0号Title 是空白的 _loop_ppu_2: STA $2007 DEX BNE _loop_ppu_2 DEY BNE _loop_ppu_1
我先择使用第00页背景,地址是$2000,包括它的命名表和属性表。向先向端口$2006写入$2000。
当下是将命名表和属性表都清零。我这里使用两重循环,共$0400次。那就是从$2000到$23FF。
我设计CHR时,将0号Title设计成空白。那就是说清零后,命名表用0号Title填满,属性表用0号调色板填满。
六、ppu操作之绘画背景
LDA #<bg_nam STA vNamL LDA #>bg_nam STA vNamH LDA #$20 STA $2006 LDA #$00 STA $2006 LDY #$00 LDX #$04 _loop_ppu_4: LDA (vNamL),y STA $2007 INY BNE _loop_ppu_4 INC vNamH DEX BNE _loop_ppu_4
背景数据,我放在bg_nam这个地址。我将它的低位和高位赋给vNamL和vNamH。
再用两重循环,这次是Y递增的,那就是从$2000+$00到$2000+$FF,重复4次。即从$2000到$23FF。
将从bg_nam开始的数据,连续写入ppu。因为我一直都是在黑屏下操用的。这是可以的。静态画面可以这样做。
七、ppu操作之对齐画面
LDX #$00 STX $2005 STX $2005
前几章,应该说过,向PPU的背景页写入数据,画面就会移位。这个是代码是纠正移位的。(滚屏也用到它,但本例没有滚屏,所以不多提。)
八、设置apu(音乐)参数
LDA #$0f ; 声音切换:0000 1111,使能:方波#1#2,三角波,噪声
STA $4015
LDA #$8f
STA $4000 ; APU方波#1控制端口
LDA #$00
STA $4001 ; APU方波#1控制端口
开启音乐功能,4个声道我都打开了,但本例只使用一个声道 方波1#
九、启动NMI中断,并设置图库
LDA #$88 ; 开nmi中断,(加入画面)图库:精灵用$1000,背景用$0000
STA $2000
本例只用背景的CHR图库。没设定这个值之前,不能开屏,因为没设定的话也是随机的。
十、ppu操作之开屏
_vb3:
BIT $2002
BPL _vb3
LDA #$08
STA $2001
开屏之前,要等一个帧信号,在屏幕扫描开始时开屏。
十一、主程序
main:
jmp main
这是一个静态贺卡,没有什么手柄操作和动态画面,所以主程序什么也没有。
十二、NMI中断和音乐播放
nmi:
inc COUNTER ; 计数器增加
ldx MUSIC_OFFSET1
lda music1,x ; 取得音长
cmp COUNTER ; 判断是否该播放
beq next2 ; 播放
cmp #$ff
beq next1 ; 是否结束
jmp over ; 退出
next1:
ldx #$00 ; 回到开始
stx MUSIC_OFFSET1
jmp over
next2:
lda #$00
sta COUNTER
lda MUSIC_OFFSET2
tay
lda music2,y ; 波长低八位
cmp #$ff ; 结束了吗?
beq next3
sta $4002
iny
lda music2,y ; 波长高三位+音长计数器
cmp #$ff ; 结束了吗?
beq next3
sta $4003
iny ; 下一个音符
sty MUSIC_OFFSET2
inx
stx MUSIC_OFFSET1
jmp over
next3:
ldy #$00 ; 回到开始
sty MUSIC_OFFSET2
over:
rti
music1:
; 音长 8*4+1=33
.db $10,$10,$20,$20,$20,$20,$20,$10
.db $10,$20,$20,$20,$20,$20,$20,$10
.db $10,$20,$20,$20,$20,$20,$20,$20
.db $20,$10,$10,$20,$20,$20,$20,$20
.db $20,$FF
music2:
; 两个一组,$FF为结束 8*8+2=66
.db $1C,$09,$1C,$09,$FD,$08,$1C,$09
.db $D4,$08,$E1,$08,$00,$00,$1C,$09
.db $1C,$D9,$FD,$08,$1C,$09,$BD,$08
.db $D4,$08,$D4,$08,$00,$00,$1C,$09
.db $1C,$09,$8D,$08,$A8,$08,$D4,$08
.db $E1,$08,$FD,$08,$FD,$08,$FD,$08
.db $00,$00,$9F,$08,$9F,$08,$A8,$08
.db $D4,$08,$BD,$08,$D4,$08,$D4,$D4
.db $08,$00,$FF,$FF
因为主程序什么也没有,所以中断的数据现场保护(进出栈)就全都省了。
这个音乐播放也是很简单,取一个音长,再取两个频率值的高低位,写入端口,累计音长值(就是帧数),音长结束则更新下一组数据。如此循环。
十三、背景画面数据
bg_pal: .include "bg_pal.inc" bg_nam: .include "bg_nam.inc" .db $00,$00,$00,$00
调色板 :bg_pal
背景的命名表和属性表:bg_nam
这两个都是通过我的背景编辑工具,编制出来的。见前面章节的讲解。
十四、IRQ
irq:
rti
本例不使用。
十五、启动的指针
.org $fffa
.dw nmi,reset,irq
这三个地址很重要,也很简单。前面章节说过了。
源代码和工程下载:地址 http://fogota.ysepan.com/
找到 NES Tool Kit文件夹,各章节的相关下载都是在一起。
编译和组成NES的办法,前面章节的讲的。相关工具,在第一章和第六章就已有下载。
浙公网安备 33010602011771号