[FC旋风][nes播放wav音频]
;[MMC5][PCM声音播放][128kbps][20190527]
;FlameCyclone
;========================================
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地址高位
;========================================
INIT_SPEED = $11 ;默认速度
;$11 = 128kbps ;112周期写入一次
;========================================
.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
LDA $00
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
LDA $8000
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 "2019.05.27"
.DB $FF
;========================================
WaveInfo: ;标题文字
.DB MODE_CNTL,$21,$C4
.STR "NAME: YUN GONG XUN YIN"
.DB MODE_CNTS,$44
.STR "YEAR: 1986"
.DB MODE_CNTS,$44
.STR "ARTIST: XU JING QING"
.DB MODE_CNTS,$44
.STR "DURATION: 02:54"
.DB MODE_CNTS,$44
.STR "BIT RATE: 128KBPS"
.DB $FF
;========================================
.ORG $FFEF
NmiProcess:
RTI
.ORG $FFFA
NMI:
.DB <NmiProcess
.DB >NmiProcess
RESET:
.DB <ResetProcess
.DB >ResetProcess
IRQ:
.DB <NmiProcess
.DB >NmiProcess
歌曲采样率 = (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