[FC][BestVisible][Config].asm
;文件头配置
NES_16KB_PRG_SIZE = 8
NES_8KB_CHR_SIZE = 16
PRG_BANK_MASK = NES_16KB_PRG_SIZE * 2 - 1 ;bank号掩码
PRG_BANK_E000 = NES_16KB_PRG_SIZE * 2 - 1
PRG_BANK_C000 = NES_16KB_PRG_SIZE * 2 - 2
;文件头
;======================================================================
.INESPRG NES_16KB_PRG_SIZE ;16KB PRG 数量
.INESCHR NES_8KB_CHR_SIZE ;8KB CHR 数量
.INESMAP 4 ;Mapper号
.INESSUBMAP 0 ;子Mapper号
.INESMIR 0 ;命名表镜像 0: 水平 1: 垂直 2: 硬连线四屏
.INESPRGRAM 7 ;PRG RAM 大小
.INESPRGNVRAM 7 ;PRG NVRAM(非易失性)的大小。
.INESCHRRAM 0 ;CHR RAM大小
.INESCHRNVRAM 0 ;CHR NVRAM(非易失性)大小
.INESBAT 0 ;电池 和其他非易失性存储器 0: 无, 1: 存在
.INESTIM 0 ;CPU/PPU 时间 0: NTSC 1: PAL 2: 多区域 3: Dendy
;==================================================
FC_DOUBLE_PLAYERS = 1 ;是否双人玩家
FC_CHAR_MODE = 1 ;是否启用字符映射
FC_GAMEPAD_BURSTS = 1 ;是否按键连发
;==================================================
FC_PAGE_SIZE = 10 ;翻页属性
FC_ITEM_INTERVAL = 2 ;条目显示间隔行数
FC_PAGE_BUF_SIZE = 2 ;PPU单次写入缓冲项数
FC_PAGE_INIT_BUF_SIZE = 4 ;PPU单次写入缓冲项数
FC_ITEM_SUM = 18 - 1 ;显示项最大索引属性
FC_PAGE_MAX = FC_ITEM_SUM/FC_PAGE_SIZE*FC_PAGE_SIZE;最大页索引属性
FC_ITEM_POS = $20C5 ;条目显示起始位置
;==================================================
FC_CHAR_MAX = $5F
FC_CURSOR_TILE = $3E ;光标图形号
FC_OFF_TILE = $3C ;关闭状态图案
FC_ON_TILE = $3D ;开启状态图案
FC_OFF_DOUBLE_TILE = $37 ;共享关闭状态图案
FC_ON_DOUBLE_TILE = $39 ;共享开启状态图案
FC_CURSOR_OFFSET_X = 12 ;光标横坐标偏移像素值
FC_CURSOR_OFFSET_Y = 1 ;光标纵坐标偏移像素值
FC_FUNCTION_OFFSET = 21 ;状态图案横坐标偏移
;==================================================
Rom_Ppu_Ctrl = $FF ;ROM 的 PPU 控制缓冲
Rom_Ppu_Mask = $FE ;ROM 的 PPU 控制缓冲
Rom_Gamepad_Once = $F5 ;ROM 的 单次按键 地址
Rom_Gamepad_Keep = $F9 ;ROM 的 长按按键 地址
Rom_Pause_Addr = $25 ;ROM 的 暂停 地址
Rom_Pause_Val = $01 ;ROM 的 暂停 值
Rom_Pal_Addr = $07C0 ;ROM 的 调色板 值
Rom_Sprites_Buf = $0200 ;精灵缓冲地址
Stack_Bak_Addr = $6000 ;栈备份位置
Zero_Page_Bak_Addr = $6200 ;零页占用备份位置
PPU_Used_Bak_Addr = $6300 ;PPU缓冲占用备份位置
PPU_Name_Table_Bak_Addr = $6400 ;命名表备份位置
FC_Stack_Last = $6004 ;记录栈指针位置
FC_Page_Index_Last = $6002 ;记录上次页面索引
FC_Item_Index_Last = $6003 ;记录上次选项索引
FC_PPU_Buffer = $0300 ;PPU缓冲地址
;==================================================
;主程序配置
PROGRAM_BANK = $0A
PROGRAM_ADDR = $9000
;==================================================
;可视化返回处理
MACRO_ROM_VISIBLE_RETURN .MACRO
LDA <Rom_Gamepad_Once
AND #GAMEPAD_START
.ENDM
;==================================================
;启用电池备份
MACRO_PRG_RAM_ENABLE .MACRO
LDA #$80
STA $A001
.ENDM
;==================================================
;等待音乐停止处理
MACRO_ROM_WAIT_SOUND_STOP .MACRO
.Wait:
JSR Wait_For_VBlank
JSR Music_Play
LDA $010A
CMP #$74
BEQ .Wait
.ENDM
;==================================================
;零页内存地址配置
Use_Ram_Begin = Rom_Pause_Addr + 1
.RSSET Use_Ram_Begin
FC_PPU_Buf_Pos .RS 1 ;PPU 缓冲写入位置
FC_PPU_Status .RS 1 ;PPU 显示状态
FC_Data_L .RS 1
FC_Data_H .RS 1
FC_Data_Tmp .RS 1
FC_Data_Cnt .RS 1 ;PPU 缓冲计写入数器
;==================================================
;选择项地址配置
FC_Select_Index .RS 1 ;当前选项索引
FC_Select_Temp .RS 1 ;临时索引
;==================================================
;光标配置
FC_Cursor_Addr_L .RS 1 ;光标地址低位
FC_Cursor_Addr_H .RS 1 ;光标地址高位
FC_Cursor_X .RS 1 ;光标横坐标
FC_Cursor_Y .RS 1 ;光标纵坐标
;==================================================
;翻页配置
FC_Page_Old .RS 1 ;旧的页起始索引
FC_Page_Cur .RS 1 ;当前页起始索引
FC_Page_Item_Count .RS 1 ;页条目计数
FC_Page_Item_Buffer .RS 1 ;页号缓存
FC_Page_Update_Max .RS 1 ;条目缓冲阈值
;==================================================
;按键配置
FC_Gamepad_Keep .RS 2 ;长按状态
FC_Gamepad_Once .RS 2 ;单次按下
FC_Gamepad_Temp .RS 2 ;按键缓冲
FC_Gamepad_Buf .RS 2 ;按键缓冲
FC_Gamepad_Status .RS 2 ;按键读取状态
FC_Gamepad_Merge .RS 1 ;单次按下合并
FC_Gamepad_Bursts_Delay .RS 1 ;按键连发延迟
FC_Time_Delay .RS 1 ;延迟计时器
Use_Ram_End .RS 1 ;占用RAM地址
USE_RAM_SIZE = Use_Ram_End - Use_Ram_Begin
;==================================================
;按键键值常量
GAMEPAD_BURSTS_DELAY = $20 ;连发延迟触发值
GAMEPAD_BURSTS_SPEED = $08 ;连发触发后速度
GAMEPAD_UP = $08 ;上
GAMEPAD_DOWN = $04 ;下
GAMEPAD_LEFT = $02 ;左
GAMEPAD_RIGHT = $01 ;右
GAMEPAD_SELECT = $20 ;选择
GAMEPAD_START = $10 ;开始
GAMEPAD_B = $40 ;B
GAMEPAD_A = $80 ;A
;==================================================
;NES端口常量
PPU_CTRL = $2000 ;PPU控制寄存器
PPU_MASK = $2001 ;PPU掩码寄存器
PPU_STATUS = $2002 ;PPU状态寄存器:读取后PPU_SCROLL和PPU_ADDRESS被复位,下一个写到PPU_SCROLL的数据是水平的,写到PPU_ADDRESS的数据是高位
PPU_OAM_ADDR = $2003 ;精灵RAM地址:用来设置通过PPU_OAM_DATA访问的256字节精灵RAM地址。每次访问PPU_OAM_DATA后该地址增加1
PPU_OAM_DATA = $2004 ;精灵RAM数据:用来读/写精灵内存。地址通过PPU_OAM_ADDR来设置,每次访问后地址增加1
PPU_SCROLL = $2005 ;屏幕滚动偏移:第一个写的值会进入垂直滚动寄存器(若>239,被忽略)。第二个值出现在水平滚动寄存器
PPU_ADDRESS = $2006 ;VRAM地址:设置PPU_DATA访问的VRAM地址。第一个写地址的高6位。第二个写低8位。每次访问PPU_DATA后地址增加
PPU_DATA = $2007 ;VRAM数据:用来访问VRAM数据,通过PPU_ADDRESS设置的地址在每次访问之后会增加1或32
OAM_DMA = $4014 ;DMA访问精灵RAM:通过写一个值xx到这个端口,引起CPU内存地址为$xx00-$xxFF的区域传送到精灵内存
APU_STATUS = $4015 ;声音通道切换
JOY1_FRAME = $4016 ;手柄1 + 选通
JOY2_FRAME = $4017 ;手柄2 + 选通
[FC][BestVisible][Main].asm
;[FC][BestVisible]
;FlameCyclone
;20231207
.INCLUDE "[FC][BestVisible][Config].asm"
.INCLUDE "[FC][BestVisible][Data].asm"
;==================================================
;程序开始地址
.BANK PROGRAM_BANK & PRG_BANK_MASK
.ORG PROGRAM_ADDR
;==================================================
;程序入口
VisibleProcess
;非暂停状态直接结束
LDA <Rom_Pause_Addr
AND #Rom_Pause_Val
BEQ .VisibleProcess_End
;未按下选择键直接结束
LDA <Rom_Gamepad_Once
CMP #GAMEPAD_SELECT
BNE .VisibleProcess_End
PHA
TXA
PHA
TYA
PHA
MACRO_PRG_RAM_ENABLE;启用 PRG RAM
;禁用NMI中断
LDA <Rom_Ppu_Ctrl
AND #$7F
STA PPU_CTRL
;备份栈, 防止栈深度过大导致异常
TSX
STX FC_Stack_Last
.Back_Stack
LDA $0100,X
STA Stack_Bak_Addr,X
INX
BNE .Back_Stack
LDX #$FF
TXS
JSR Wait_Sound_Stop;等待音乐结束
JSR Backup_Data;备份数据
;备份占用的几个地址
LDA <FC_PPU_Status
PHA
;载入上次的页起始索引与功能选项索引
LDA FC_Page_Index_Last
STA <FC_Page_Cur
LDA FC_Item_Index_Last
STA <FC_Select_Index
JSR Program_Begin;可视化菜单开始
;记录本次的页起始索引与功能选项索引
LDA <FC_Select_Index
STA FC_Item_Index_Last
LDA <FC_Page_Cur
STA FC_Page_Index_Last
JSR Recovery_Data;恢复数据
;恢复占用的几个地址
PLA
STA <FC_PPU_Status
;恢复栈
LDX FC_Stack_Last
.Recovery_Stack
LDA Stack_Bak_Addr,X
STA $0100,X
INX
BNE .Recovery_Stack
LDX FC_Stack_Last
TXS
;防止触发开始键
LDA #GAMEPAD_START
STA <Rom_Gamepad_Keep
LDA #$00
STA <Rom_Gamepad_Once
;恢复屏幕显示控制
LDA <Rom_Ppu_Ctrl
STA PPU_CTRL
PLA
TAY
PLA
TAX
PLA
.VisibleProcess_End
MACRO_ROM_VISIBLE_RETURN
RTS
;==================================================
;等待音乐结束
Wait_Sound_Stop
MACRO_ROM_WAIT_SOUND_STOP
RTS
;==================================================
;备份数据
Backup_Data
JSR Disable_Nmi_And_Wait_For_VBlank;等待VBlank
JSR Clear_PPU_Palette;(调色板清理)331周期
JSR Backup_Zero_Page_RAM;备份零页内存(3597周期)
JSR Backup_PPU_Buf_Used;备份PPU缓冲占用内存(3597周期)
JSR Init_Zero_Page_RAM;初始化零页占用内存(289周期)
JSR Backup_PPU_2000_23FF;备份命名表(15465周期)
JSR Init_OAM_RAM;初始化精灵内存(2323周期)
JSR Clear_PPU_2000_23FF;清空命名表(9275周期)
RTS
;==================================================
;恢复数据
Recovery_Data
JSR Disable_Nmi_And_Wait_For_VBlank;等待VBlank
JSR Recovery_PPU_Palette;恢复调色板(521周期)
JSR Recovery_PPU_2000_23FF;恢复命名表(14440周期)
JSR Recovery_PPU_Buf_Used;恢复PPU缓冲占用内存(3597周期)
JSR Recovery_Zero_Page_RAM;恢复零页备份(3341周期)
RTS
;==================================================
;禁用NMI并且等待VBlank
Disable_Nmi_And_Wait_For_VBlank
LDA #$00
STA PPU_CTRL
STA PPU_MASK
STA <FC_PPU_Status
;==================================================
;等待VBlank中断
Wait_For_VBlank
LDA PPU_STATUS
BPL Wait_For_VBlank
;LDA <FC_Time_Delay
;BEQ .End
;DEC <FC_Time_Delay
.End
RTS
;==================================================
;清空命名表
Clear_PPU_2000_23FF
JSR Set_PPU_Name_Table_Addr
LDX #$00
LDY #$04
TXA
.Write
STA PPU_DATA
DEX
BNE .Write
DEY
BNE .Write
RTS
;==================================================
;设置PPU命名表地址
Set_PPU_Name_Table_Addr
;LDA PPU_STATUS
LDA #$20
STA PPU_ADDRESS
LDA #$00
STA PPU_ADDRESS
RTS
;==================================================
;设置PPU调色板地址
Set_PPU_Palette_Addr
;LDA PPU_STATUS
LDA #$3F
STA PPU_ADDRESS
LDA #$00
STA PPU_ADDRESS
RTS
;==================================================
Get_Name_Table_Bak_Addr_And_Size
LDA #LOW(PPU_Name_Table_Bak_Addr)
STA <FC_Data_L
LDA #HIGH(PPU_Name_Table_Bak_Addr)
STA <FC_Data_H
LDY #$00
LDX #$04
RTS
;==================================================
;备份PPU命名表
Backup_PPU_2000_23FF
JSR Set_PPU_Name_Table_Addr
JSR Get_Name_Table_Bak_Addr_And_Size
LDA PPU_DATA
.Write
LDA PPU_DATA
STA [FC_Data_L],Y
INY
BNE .Write
INC <FC_Data_H
DEX
BNE .Write
RTS
;==================================================
;恢复PPU命名表
Recovery_PPU_2000_23FF
JSR Set_PPU_Name_Table_Addr
JSR Get_Name_Table_Bak_Addr_And_Size
.Write
LDA [FC_Data_L],Y
STA PPU_DATA
INY
BNE .Write
INC <FC_Data_H
DEX
BNE .Write
RTS
;==================================================
;备份零页占用RAM
Backup_Zero_Page_RAM
LDX #$00
.Write
LDA <$00,X
STA Zero_Page_Bak_Addr,X
INX
BNE .Write
RTS
;==================================================
;恢复零页占用RAM
Recovery_Zero_Page_RAM
LDX #$00
.Write
LDA Zero_Page_Bak_Addr,X
STA <$00,X
INX
BNE .Write
RTS
;==================================================
;初始化零页占用RAM
Init_Zero_Page_RAM
LDX #USE_RAM_SIZE - 1
LDA #$00
.Write
STA <Use_Ram_Begin,X
DEX
BNE .Write
STA <Use_Ram_Begin,X
RTS
;==================================================
;备份PPU缓冲占用
Backup_PPU_Buf_Used
LDX #$00
.Write
LDA FC_PPU_Buffer,X
STA PPU_Used_Bak_Addr,X
INX
BNE .Write
RTS
;==================================================
;恢复PPU缓冲占用
Recovery_PPU_Buf_Used
LDX #$00
.Write
LDA PPU_Used_Bak_Addr,X
STA FC_PPU_Buffer,X
INX
BNE .Write
RTS
;==================================================
;初始化精灵内存
Init_OAM_RAM
LDX #$00
STX PPU_OAM_ADDR
LDA #$F8
.Write
STA PPU_OAM_DATA
INX
BNE .Write
RTS
;==================================================
;恢复调色板
Recovery_PPU_Palette
JSR Set_PPU_Palette_Addr
LDX #$00
.Write
LDA Rom_Pal_Addr,X
STA PPU_DATA
INX
CPX #$20
BCC .Write
RTS
;==================================================
;初始化调色板
Init_PPU_Palette
JSR Set_PPU_Palette_Addr
LDX #$00
.Write
LDA Palette_Data,X
STA PPU_DATA
INX
CPX #$20
BCC .Write
RTS
;==================================================
;清空调色板
Clear_PPU_Palette
JSR Set_PPU_Palette_Addr
LDX #$20
LDA #$0F
.Write
STA PPU_DATA
DEX
BNE .Write
RTS
;==================================================
;设置CHR图形
Init_CHR_Bank
LDX #$05
.Write
STX $8000
LDA CHR_Data,x
STA $8001
DEX
BPL .Write
RTS
.INCLUDE "[FC][BestVisible][Cursor].asm"
.INCLUDE "[FC][BestVisible][Function].asm"
.INCLUDE "[FC][BestVisible][PPU].asm"
.INCLUDE "[FC][BestVisible][Gamepad].asm"
.INCLUDE "[FC][BestVisible][Page].asm"
.INCLUDE "[FC][BestVisible][Text].asm"
;==================================================
;程序开始
Program_Begin
JSR Init_CHR_Bank;初始化图形Bank
JSR Init_PPU_Palette;设置调色板
;设置页选项缓冲数量
LDA #FC_PAGE_INIT_BUF_SIZE
STA <FC_Page_Update_Max
JSR Page_Text_Update;更新页选项内容
;开启显示
LDA #$1E
STA <FC_PPU_Status
;禁用触发选择键与开始键
LDA #GAMEPAD_SELECT | GAMEPAD_START
STA <FC_Gamepad_Buf
;==================================================
;循环等待(直到按下开始键)
Main_Loop
JSR VBlank_Proc;VBlank处理
JSR Gamepad_Process;按键处理
.IF FC_GAMEPAD_BURSTS
JSR Gamepad_Bursts;按键超时连发处理
.ENDIF
LDA <FC_Gamepad_Once
.IF 0 = FC_DOUBLE_PLAYERS
ORA <FC_Gamepad_Once + 1
.ENDIF
STA <FC_Gamepad_Merge
JSR Select_Item_Proc;选择选项处理
JSR Function_State_Proc;功能状态处理
;返回处理
LDA <FC_Gamepad_Keep
CMP <FC_Gamepad_Once
BNE Main_Loop
LDA <FC_Gamepad_Keep + 1
CMP <FC_Gamepad_Once + 1
BNE Main_Loop
LDA <FC_Gamepad_Once
ORA <FC_Gamepad_Once + 1
CMP #GAMEPAD_START
BNE Main_Loop
RTS
;==================================================
;VBlank处理
VBlank_Proc
LDA <FC_PPU_Status
BEQ .End
JSR Wait_For_VBlank
.End
JSR FC_PPU_Process;PPU处理
JSR Show_Select_Cursor;光标处理
RTS
;==================================================
;选择选项处理
Select_Item_Proc
.Pre_Page;上一页
LDA <FC_Gamepad_Merge
AND #GAMEPAD_LEFT
BEQ .Next_Page
JSR Switch_To_Pre_Page
.Next_Page;下一页
LDA <FC_Gamepad_Merge
AND #GAMEPAD_RIGHT
BEQ .Pre_Item
JSR Switch_To_Next_Page
.Pre_Item;前一项
LDA <FC_Gamepad_Merge
AND #GAMEPAD_UP
BEQ .Next_Item
JSR Switch_To_Pre_Item
.Next_Item;后一项
LDA <FC_Gamepad_Merge
.IF FC_DOUBLE_PLAYERS
AND #GAMEPAD_DOWN
.ELSE
AND #GAMEPAD_SELECT | GAMEPAD_DOWN
.ENDIF
BEQ .End
JSR Switch_To_Next_Item
.End
RTS
;==================================================
;功能状态处理
Function_State_Proc
LDX #$01
.Start
.Enable;启用功能
.IF FC_DOUBLE_PLAYERS
LDA <FC_Gamepad_Once,X
.ELSE
LDA <FC_Gamepad_Merge
.ENDIF
CMP #GAMEPAD_B
BNE .Disable
JSR Function_State_Enable
JSR Function_State_Show
.Disable;禁用功能
.IF FC_DOUBLE_PLAYERS
LDA <FC_Gamepad_Once,X
.ELSE
LDA <FC_Gamepad_Merge
.ENDIF
CMP #GAMEPAD_A
BNE .Flip
JSR Function_State_Disable
JSR Function_State_Show
.Flip;翻转功能状态
.IF FC_DOUBLE_PLAYERS
LDA <FC_Gamepad_Once,X
CMP #GAMEPAD_SELECT
BNE .Next
JSR Function_State_Flip
JSR Function_State_Show
.ENDIF
.Next
DEX
BPL .Start
.End
RTS
;==============================
;可视化功能初始化
Visible_Init
LDA #$00
STA PPU_CTRL
;清空栈数据
TSX
LDA #$00
.Init_Stack;初始化栈
STA $0100,X
DEX
BNE .Init_Stack
STA $0100,X
MACRO_PRG_RAM_ENABLE;启用 PRG RAM
.Init_PRG_RAM;初始化PRG RAM
LDA #$60
STA <$01
LDA #$00
STA <$00
TAY
LDX #$20
.Write_Data;写入数据
STA [$00],Y
INY
BNE .Write_Data
INC <$01
DEX
BNE .Write_Data
LDX #$02
.Wait_For_VBlank;等待VBlank
LDA PPU_STATUS
BPL .Wait_For_VBlank
DEX
BNE .Wait_For_VBlank
LDA <Rom_Ppu_Ctrl
STA PPU_CTRL
RTS
.INCLUDE "[FC][BestVisible][Static].asm"
.INCLUDE "[FC][BestVisible][Game].asm"