[FC][MMC5][PCM声音播放][可选码率]

;[MMC5][PCM声音播放][可选码率][20210303]
;FlameCyclone

;========================================
;歌曲采样率 = (3048KB * 1024)(字节) / 时长(秒数)
; 码率 = CPU主频/采样率 = 写入PCM端口间隔周期
;320kbps = 1789772.5/40000 = 44.74
;312kbps = 1789772.5/39000 = 45.89
;304kbps = 1789772.5/38000 = 47.09
;296kbps = 1789772.5/37000 = 48.37
;288kbps = 1789772.5/36000 = 49.71
;280kbps = 1789772.5/35000 = 51.13
;272kbps = 1789772.5/34000 = 52.64
;264kbps = 1789772.5/33000 = 54.23
;256kbps = 1789772.5/32000 = 55.93
;248kbps = 1789772.5/31000 = 57.73
;240kbps = 1789772.5/30000 = 59.65
;232kbps = 1789772.5/29000 = 61.71
;224kbps = 1789772.5/28000 = 63.92
;216kbps = 1789772.5/27000 = 66.28
;208kbps = 1789772.5/26000 = 68.83
;200kbps = 1789772.5/25000 = 71.59
;192kbps = 1789772.5/24000 = 74.57
;184kbps = 1789772.5/23000 = 77.81
;176kbps = 1789772.5/22000 = 81.35
;168kbps = 1789772.5/21000 = 85.22
;160kbps = 1789772.5/20000 = 89.48
;172kbps = 1789772.5/19000 = 94.19
;144kbps = 1789772.5/18000 = 99.43
;136kbps = 1789772.5/17000 = 105.28
;128kbps = 1789772.5/16000 = 111.86
;120kbps = 1789772.5/15000 = 119.31
;112kbps = 1789772.5/14000 = 127.84
;104kbps = 1789772.5/13000 = 137.67
; 96kbps = 1789772.5/12000 = 149.14
; 88kbps = 1789772.5/11000 = 162.70
; 80kbps = 1789772.5/10000 = 178.97
; 72kbps = 1789772.5/09000 = 198.86
; 64kbps = 1789772.5/08000 = 223.72
; 56kbps = 1789772.5/07000 = 255.68
; 48kbps = 1789772.5/06000 = 298.29
; 40kbps = 1789772.5/05000 = 357.95
; 32kbps = 1789772.5/04000 = 447.44
; 24kbps = 1789772.5/03000 = 596.59
; 16kbps = 1789772.5/02000 = 894.88
; 8kbps = 1789772.5/01000 = 1789.77
;========================================
;比特率设置(32,40,48,56,64,72,80,96,112,128,144,160,192,224,240,256,320)
KBPS = 72
;========================================
PCM_Data_L = $80 ;数据低位
PCM_Data_H = PCM_Data_L + $01 ;数据高位
Bank_L = PCM_Data_H + $01 ;Bank索引低位
Bank_H = Bank_L + $01 ;Bank索引高位
Play_Speed = Bank_H + $01 ;播放速率
;============================================================
FC_Data_L = Play_Speed + 1 ;数据地址低位
FC_Data_H = FC_Data_L+$01 ;数据地址高位
FC_Data_Cnt = FC_Data_H+$01 ;数据计数
;============================================================
FC_PPU_Buffer = $0200 ;PPU缓冲地址
FC_PPU_Status = FC_PPU_Buffer+$C0 ;PPU显示状态
FC_PPU_Cursor = FC_PPU_Status + $01 ;当前PPU缓冲位置
FC_PPU_Addr_L = FC_PPU_Cursor + $01 ;PPU地址低位
FC_PPU_Addr_H = FC_PPU_Addr_L + $01 ;PPU地址高位
;========================================
;CPU播放周期
.IF KBPS == 32
INIT_SPEED = $54
.ENDIF
.IF KBPS == 40
INIT_SPEED = $42
.ENDIF
.IF KBPS == 48
INIT_SPEED = $36
.ENDIF
.IF KBPS == 56
INIT_SPEED = $2E
.ENDIF
.IF KBPS == 64
INIT_SPEED = $27
.ENDIF
.IF KBPS == 72
INIT_SPEED = $22
.ENDIF
.IF KBPS == 80
INIT_SPEED = $1E
.ENDIF
.IF KBPS == 96
INIT_SPEED = $18
.ENDIF
.IF KBPS == 112
INIT_SPEED = $14
.ENDIF
.IF KBPS == 128
INIT_SPEED = $11
.ENDIF
.IF KBPS == 144
INIT_SPEED = $0E
.ENDIF
.IF KBPS == 160
INIT_SPEED = $0C
.ENDIF
.IF KBPS == 192
INIT_SPEED = $09
.ENDIF
.IF KBPS == 224
INIT_SPEED = $07
.ENDIF
.IF KBPS == 240
INIT_SPEED = $06
.ENDIF
.IF KBPS == 256
INIT_SPEED = $06
.ENDIF
.IF KBPS == 320
INIT_SPEED = $03
.ENDIF
;========================================
;指令延迟
DELAY_2 .MACRO
NOP
.ENDM
DELAY_3 .MACRO
LDA $00
.ENDM
DELAY_4 .MACRO
NOP
NOP
.ENDM
DELAY_5 .MACRO
NOP
LDA $00
.ENDM
DELAY_6 .MACRO
NOP
NOP
NOP
.ENDM
DELAY_7 .MACRO
NOP
NOP
LDA $00
.ENDM
;========================================
.ORG $FC00
;========================================
ResetProcess: ;程序开始
SEI
CLD
LDX #$FF
TXS
LDA #$10
STA $2000
LDA #$00
STA $2001
LDA #$00
STA $5010
ResetDelay:
LDA $2002
BPL ResetDelay
LDA $2002
BMI ResetDelay
;========================================
RamInit: ;内存初始化
LDX #$00
TXA
STA $00
STA $01
LDY #$00
RamInitSet:
STA ($00),Y
INY
BNE RamInitSet
INC $01
INX
CPX #$08
BCC RamInitSet
;========================================
PaletteClear: ;调色板清空
LDA #$3F
STA $2006
LDA #$00
STA $2006
LDX #$00
PaletteClearSet:
LDA PaletteData,X
STA $2007
INX
CPX #$20
BCC PaletteClearSet
;========================================
PortInit: ;端口初始化
LDA #$00
STA $5105
LDA #$03
STA $5100
LDA #$80
STA $5114
LDA #INIT_SPEED
STA Play_Speed
LDA #$01
STA $5101
LDA #$00
STA $5130
LDA #$00
STA $512B
STA $5127
LDA #$00
STA $5130
LDA #$01
STA $5123
LDA #$0E
STA FC_PPU_Status
JSR WriteText
;========================================
PrgInit: ;Prg初始化
LDA #$00
STA PCM_Data_L
LDA #$80
STA PCM_Data_H
LDA #$00
STA Bank_L
STA Bank_H
LDY #$00
;========================================
PlayPrgData: ;播放PRG数据
LDX Play_Speed
PlayPrgDataDelay:
DEX
BNE PlayPrgDataDelay
.IF KBPS == 32
DELAY_3
.ENDIF
.IF KBPS == 40
DELAY_4
.ENDIF
.IF KBPS == 48
DELAY_4
.ENDIF
.IF KBPS == 56
DELAY_2
.ENDIF
.IF KBPS == 64
DELAY_5
.ENDIF
.IF KBPS == 72
DELAY_5
.ENDIF
.IF KBPS == 80
DELAY_5
.ENDIF
.IF KBPS == 96
DELAY_5
.ENDIF
.IF KBPS == 112
DELAY_4
.ENDIF
.IF KBPS == 128
DELAY_3
.ENDIF
.IF KBPS == 144
DELAY_5
.ENDIF
.IF KBPS == 160
DELAY_5
.ENDIF
.IF KBPS == 192
DELAY_6
.ENDIF
.IF KBPS == 224
DELAY_5
.ENDIF
.IF KBPS == 240
DELAY_6
.ENDIF
.IF KBPS == 256
DELAY_2
.ENDIF
.IF KBPS == 320
DELAY_6
.ENDIF
LDA (PCM_Data_L),Y
STA $5011
PlayPrgDataCount:
INY
BNE PlayPrgDataSet
INC PCM_Data_H
PlayPrgDataSet:
LDA PCM_Data_H
CMP #$A0
BNE PlayPrgData
INC Bank_L
LDA Bank_L
CMP #$7F
BEQ ChrInit
ORA #$80
STA $5114
LDA #$80
STA PCM_Data_H
BNE PlayPrgData
;========================================
ChrInit: ;Chr初始化
LDA #$01
STA Bank_L
LDA #$00
STA PCM_Data_H
STA $2001
LDA $2002
LDA #$00
STA $2006
STA $2006
LDA $2007
;========================================
PlayChrData: ;播放CHR数据
LDX Play_Speed
PlayChrDataDelay:
DEX
BNE PlayChrDataDelay
.IF KBPS == 32
DELAY_4
.ENDIF
.IF KBPS == 40
DELAY_5
.ENDIF
.IF KBPS == 48
DELAY_5
.ENDIF
.IF KBPS == 56
DELAY_3
.ENDIF
.IF KBPS == 64
DELAY_6
.ENDIF
.IF KBPS == 72
DELAY_6
.ENDIF
.IF KBPS == 80
DELAY_6
.ENDIF
.IF KBPS == 96
DELAY_6
.ENDIF
.IF KBPS == 112
DELAY_5
.ENDIF
.IF KBPS == 128
DELAY_4
.ENDIF
.IF KBPS == 144
DELAY_6
.ENDIF
.IF KBPS == 160
DELAY_6
.ENDIF
.IF KBPS == 192
DELAY_7
.ENDIF
.IF KBPS == 224
DELAY_6
.ENDIF
.IF KBPS == 240
DELAY_7
.ENDIF
.IF KBPS == 256
DELAY_3
.ENDIF
.IF KBPS == 320
DELAY_7
.ENDIF
LDA $2007
STA $5011
PlayChrDataCount:
INY
BNE PlayChrDataSet
INC PCM_Data_H
PlayChrDataSet:
LDA PCM_Data_H
CMP #$10
BNE PlayChrData
INC Bank_L
BNE PlayChrDataBankSet
INC Bank_H
LDA Bank_H
CMP #$02
BEQ PlayOver
PlayChrDataBankSet:
LDA Bank_H
STA $5130
LDA Bank_L
STA $5123
LDA #$00
STA $2006
STA $2006
STA PCM_Data_H
LDA $2002
LDA $2007
LDA #$00
BEQ PlayChrData
;========================================
PlayOver: ;播放结束
JMP ($FFFC)
;========================================
MODE_CNTL = $FB ;行写入模式
MODE_CNTC = $FC ;行清除模式
MODE_CNTN = $FD ;行转行模式
MODE_CNTS = $FE ;转行空格写入模式
;----------------------------------------
FC_PPU_Process: ;PPU处理
LDA FC_PPU_Buffer
BEQ FC_PPU_Process_End
JSR FC_PPU_Process_Beg
FC_PPU_Process_End:
LDA #$00
STA FC_PPU_Buffer
STA $2006
STA $2006
STA $2005
STA $2005
LDA FC_PPU_Status
STA $2001
RTS
;----------------------------------------
FC_PPU_Process_Beg: ;处理开始
LDX FC_PPU_Cursor
LDA #$00
STA $2001
STA FC_PPU_Buffer,X
STA FC_PPU_Cursor
LDX #$FF
;----------------------------------------
FC_PPU_Mode_Select: ;模式选择
INX
LDA FC_PPU_Buffer,X
CMP #MODE_CNTL
BEQ FC_PPU_Mode_CountLine
CMP #MODE_CNTC
BEQ FC_PPU_Mode_CountClear
CMP #MODE_CNTN
BEQ FC_PPU_Mode_CountNext
CMP #MODE_CNTS
BEQ FC_PPU_Mode_CountSpace
RTS
;----------------------------------------
FC_PPU_Mode_CountLine: ;行写入模式
JSR FC_PPU_Set_Addr
LDY FC_PPU_Buffer,X
FC_PPU_CountLine_Write:
INX
LDA FC_PPU_Buffer,X
STA $2007
DEY
BNE FC_PPU_CountLine_Write
JMP FC_PPU_Mode_Select
;----------------------------------------
FC_PPU_Mode_CountClear: ;行清除模式
JSR FC_PPU_Write_Addr
LDY FC_PPU_Buffer,X
LDA #$00
FC_PPU_CountClear_Write:
STA $2007
DEY
BNE FC_PPU_CountClear_Write
JMP FC_PPU_Mode_Select
;----------------------------------------
FC_PPU_Mode_CountNext: ;转行写入模式
JSR FC_PPU_Set_Next
FC_PPU_CountNext_Write:
LDA FC_PPU_Addr_H
STA $2006
LDA FC_PPU_Addr_L
STA $2006
INX
LDY FC_PPU_Buffer,X
JMP FC_PPU_CountLine_Write
;----------------------------------------
FC_PPU_Mode_CountSpace: ; 空格写入模式
INX
LDA FC_PPU_Addr_L
AND #$E0
CLC
ADC FC_PPU_Buffer,X
STA FC_PPU_Addr_L
BCC FC_PPU_Mode_CountSpace_Write
INC FC_PPU_Addr_H
FC_PPU_Mode_CountSpace_Write:
LDA FC_PPU_Addr_H
STA $2006
LDA FC_PPU_Addr_L
STA $2006
INX
LDY FC_PPU_Buffer,X
JMP FC_PPU_CountLine_Write
;----------------------------------------
FC_PPU_Set_Addr: ;写入地址
INX
LDA FC_PPU_Buffer,X
STA FC_PPU_Addr_H
STA $2006
INX
LDA FC_PPU_Buffer,X
STA FC_PPU_Addr_L
STA $2006
INX
RTS
;----------------------------------------
FC_PPU_Write_Addr: ;写入地址
INX
LDA FC_PPU_Buffer,X
STA $2006
INX
LDA FC_PPU_Buffer,X
STA $2006
INX
RTS
;----------------------------------------
FC_PPU_Set_Next: ;地址转行
LDA FC_PPU_Addr_L
CLC
ADC #$20
STA FC_PPU_Addr_L
BCC FC_PPU_Set_Next_End
INC FC_PPU_Addr_H
FC_PPU_Set_Next_End:
RTS
;==================================================
FC_PPU_Source_Write_Buffer: ;写入PPU缓冲数据
LDA (FC_Data_L),Y
STA FC_PPU_Buffer,X
INX
INY
RTS
;========================================
SRC_MODE_NULL = $FF ;无地址写入模式
;----------------------------------------
FC_PPU_Write_Source: ;源数据写入
PHA
TXA
PHA
TYA
PHA
LDX FC_PPU_Cursor
LDY #$00
LDA FC_Data_Cnt
STY FC_Data_Cnt
INC FC_Data_Cnt
CMP #SRC_MODE_NULL
BNE FC_PPU_Write_Line
JSR FC_PPU_Source_Write_Buffer
STA FC_Data_Cnt
FC_PPU_Write_Source_Set:
JSR FC_PPU_Source_Write_Buffer
DEC FC_Data_Cnt
BNE FC_PPU_Write_Source_Set
FC_PPU_Write_Line:
LDA (FC_Data_L),Y
CMP #MODE_CNTL
BNE FC_PPU_Write_Next
JSR FC_PPU_Source_Write_Buffer
JSR FC_PPU_Source_Write_Buffer
JSR FC_PPU_Source_Write_Buffer
JSR FC_PPU_Source_Write_Buffer
STA FC_Data_Cnt
BNE FC_PPU_Write_Source_Set
FC_PPU_Write_Next:
CMP #MODE_CNTN
BNE FC_PPU_Write_Space
JSR FC_PPU_Source_Write_Buffer
JSR FC_PPU_Source_Write_Buffer
STA FC_Data_Cnt
BNE FC_PPU_Write_Source_Set
FC_PPU_Write_Space:
CMP #MODE_CNTS
BNE FC_PPU_Write_Clear
JSR FC_PPU_Source_Write_Buffer
JSR FC_PPU_Source_Write_Buffer
JSR FC_PPU_Source_Write_Buffer
STA FC_Data_Cnt
BNE FC_PPU_Write_Source_Set
FC_PPU_Write_Clear:
CMP #MODE_CNTC
BNE FC_PPU_Write_Source_End
JSR FC_PPU_Source_Write_Buffer
JSR FC_PPU_Source_Write_Buffer
BNE FC_PPU_Write_Line
FC_PPU_Write_Source_End:
STX FC_PPU_Cursor
LDA #$00
STA FC_Data_Cnt
PLA
TAY
PLA
TAX
PLA
RTS
;========================================
WriteText: ;写入文本
LDA #<TtileText
STA FC_Data_L
LDA #>TtileText
STA FC_Data_H
LDA #$00
STA FC_Data_Cnt
JSR FC_PPU_Write_Source
JSR FC_PPU_Process
LDA #<WaveInfo
STA FC_Data_L
LDA #>WaveInfo
STA FC_Data_H
LDA #$00
STA FC_Data_Cnt
JSR FC_PPU_Write_Source
JSR FC_PPU_Process
RTS
;========================================
PaletteData:
.DB $0F,$24,$24,$24,$0F,$24,$24,$24,$0F,$24,$24,$24,$0F,$24,$24,$24
.DB $0F,$24,$24,$24,$0F,$24,$24,$24,$0F,$24,$24,$24,$0F,$24,$24,$24
;========================================
TtileText: ;标题文字
.DB MODE_CNTL,$20,$A8
.STR "WAVE FILE PLAYER"
.DB MODE_CNTS,$46
.STR "MAKE BY FLAMECYCLONE"
.DB MODE_CNTS,$4B
.STR "2021.03.03"
.DB $FF
;========================================
WaveInfo: ;标题文字
.DB MODE_CNTL,$21,$C4
.STR "NAME: ZHUO NI QIU"
.DB MODE_CNTS,$44
.STR "YEAR: 1997"
.DB MODE_CNTS,$44
.STR "ARTIST: ZHUO YI TING"
.DB MODE_CNTS,$44
.STR "DURATION: 02:37"
.DB MODE_CNTS,$44
.STR "BIT RATE: 160KBPS"
.DB $FF
;========================================
.ORG $FFEF
NmiProcess:
RTI
.ORG $FFFA
NMI:
.DB <NmiProcess
.DB >NmiProcess
RESET:
.DB <ResetProcess
.DB >ResetProcess
IRQ:
.DB <NmiProcess
.DB >NmiProcess