FC 可视化功能菜单主代码

发布时间:2023年12月17日

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

[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"


文章来源:https://blog.csdn.net/Flame_Cyclone/article/details/135034892
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。