【原创】NES第十波:解说一个NES音乐贺卡的源代码

我将自己写的一编音乐贺卡源代码拿来做解说。完整的工程和源代码见最后面的下载链接。

之前的解说都是保姆级的。从这一章开始,就变得简洁了。

 

一、定义内存变量(汇编都是用全局静态变量的)

1
2
3
4
5
COUNTER = $00
MUSIC_OFFSET1 = $01
MUSIC_OFFSET2 = $02
vNamL = $03
vNamH = $04

  我只用了5个字节,好省。

 

二、初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
        ;.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热机

1
2
3
4
5
6
7
            ; 原写法(热机)有点区别
_vb1:
        BIT     $2002
        BPL     _vb1
_vb2:
        BIT     $2002
        BPL     _vb2

  ppu上电比cpu慢,就是说通电了,cpu能快速启动工作,但ppu还没有达到正常工作的状态,要等一等,大约等ppu发来两次帧信号就可以了。

 

四、ppu操作之关屏和配色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
        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操作之清除背景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    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操作之绘画背景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    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操作之对齐画面

1
2
3
LDX #$00
STX $2005
STX $2005

  前几章,应该说过,向PPU的背景页写入数据,画面就会移位。这个是代码是纠正移位的。(滚屏也用到它,但本例没有滚屏,所以不多提。)

 

八、设置apu(音乐)参数

1
2
3
4
5
6
7
8
LDA     #$0f    ; 声音切换:0000 1111,使能:方波#1#2,三角波,噪声
STA     $4015
 
LDA     #$8f
STA     $4000   ; APU方波#1控制端口
 
LDA     #$00
STA     $4001   ; APU方波#1控制端口

  开启音乐功能,4个声道我都打开了,但本例只使用一个声道 方波1#

 

九、启动NMI中断,并设置图库

1
2
LDA     #$88    ; 开nmi中断,(加入画面)图库:精灵用$1000,背景用$0000
STA     $2000 

  本例只用背景的CHR图库。没设定这个值之前,不能开屏,因为没设定的话也是随机的。

 

十、ppu操作之开屏

1
2
3
4
5
6
_vb3:
    BIT $2002
    BPL _vb3
 
        LDA #$08
        STA $2001

  开屏之前,要等一个帧信号,在屏幕扫描开始时开屏。

 

十一、主程序

1
2
main:
        jmp     main

  这是一个静态贺卡,没有什么手柄操作和动态画面,所以主程序什么也没有。

 

十二、NMI中断和音乐播放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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

  因为主程序什么也没有,所以中断的数据现场保护(进出栈)就全都省了。

       这个音乐播放也是很简单,取一个音长,再取两个频率值的高低位,写入端口,累计音长值(就是帧数),音长结束则更新下一组数据。如此循环。

 

十三、背景画面数据

1
2
3
4
5
bg_pal:
    .include "bg_pal.inc"
bg_nam:
    .include "bg_nam.inc"
    .db $00,$00,$00,$00

  调色板 :bg_pal

       背景的命名表和属性表:bg_nam

       这两个都是通过我的背景编辑工具,编制出来的。见前面章节的讲解。

 

十四、IRQ

1
2
irq:
        rti

  本例不使用。

 

十五、启动的指针

1
2
.org $fffa
.dw nmi,reset,irq

  这三个地址很重要,也很简单。前面章节说过了。

 

源代码和工程下载:地址 http://fogota.ysepan.com/

找到 NES Tool Kit文件夹,各章节的相关下载都是在一起。

编译和组成NES的办法,前面章节的讲的。相关工具,在第一章和第六章就已有下载。

 

posted on   大魔司教教主  阅读(435)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示