【原创】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的办法,前面章节的讲的。相关工具,在第一章和第六章就已有下载。

 

posted on 2023-01-26 15:38  大魔司教教主  阅读(422)  评论(0编辑  收藏  举报

导航