本次IRQ研究了如下表所示Mapper的IRQ操作:
卡带名 | Mapper号 |
VRC3 | 73 |
VRC4 | 21,23,25 |
VRC6 | 24 & 26 |
VRC7 | 85 |
MMC3 | 4 |
MMC4 | 10 |
MMC5 | 5 |
Sunsoft FME-7 | 69 |
Namco163 | 19 |
Jaleco SS 88006 | 18 |
RAMBO-1 | 64 |
共计11种Mapper的IRQ操作使用例子
代码内有详细注释, 希望能帮助到感兴趣的人.
Mapper控制代码(MMC3为例)
[FC][Mapper][MMC3].asm
;==================================================
;Mapper号
MAPPER_NUMBER = 04
;==================================================
;MMC3 (Mapper 4) 寄存器常量
MAPPER_REG_BANK_CTRL = $8000
MAPPER_REG_BANK_DATA = $8001
MAPPER_REG_MIRRORING = $A000
MAPPER_REG_PRG_RAM_PROTECT = $A001
MAPPER_REG_IRQ_LATCH = $C000
MAPPER_REG_IRQ_RELOAD = $C001
MAPPER_REG_IRQ_DISABLE = $E000
MAPPER_REG_IRQ_ENABLE = $E001
;==================================================
IRQ_SCANLINE_BEGIN = 135
IRQ_SCANLINE_1 = 8
IRQ_SCANLINE_2 = 54
IRQ_SCANLINE_3 = 8
;==================================================;====================================================================================================
;宏常量
;====================================================================================================;====================================================================================================
MACRO_MAPPER_INIT .MACRO;禁用IRQSTA MAPPER_REG_IRQ_DISABLE;水平镜像LDA #$01STA MAPPER_REG_MIRRORING;初始化图形bankLDX #$05
.Init_Chr_BankSTX MAPPER_REG_BANK_CTRLLDA .ChrBankData,XSTA MAPPER_REG_BANK_DATADEXBPL .Init_Chr_BankJMP .Init_Chr_Bank_End
.ChrBankData.DB $00,$02,$04,$05,$06,$07
.Init_Chr_Bank_End;启用SRAMLDA #$80STA MAPPER_REG_PRG_RAM_PROTECT.ENDM;====================================================================================================
MACRO_MAPPER_SOUND_CLEAR .MACRO.ENDM;====================================================================================================
MACRO_SRAM_ENABLE .MACROLDA #$80STA MAPPER_REG_PRG_RAM_PROTECT.ENDM;====================================================================================================
MACRO_SWITCH_BANK_8000_A .MACROPHALDA #$06STA MAPPER_REG_BANK_CTRLPLASTA MAPPER_REG_BANK_DATA.ENDMMACRO_SWITCH_BANK_A000_A .MACROPHALDA #$07STA MAPPER_REG_BANK_CTRLPLASTA MAPPER_REG_BANK_DATA.ENDMMACRO_SWITCH_BANK_C000_A .MACRO.ENDMMACRO_SWITCH_BANK_E000_A .MACRO.ENDM;====================================================================================================
MACRO_TRIGGER_FIRST_IRQ .MACROLDA #IRQ_SCANLINE_BEGIN + 1STA MAPPER_REG_IRQ_LATCHSTA MAPPER_REG_IRQ_RELOADSTA MAPPER_REG_IRQ_ENABLECLI.ENDM;====================================================================================================
MACRO_ENABLE_IRQ .MACROSTA MAPPER_REG_IRQ_ENABLE.ENDM;====================================================================================================
MACRO_DISABLE_IRQ .MACROLDA #$00STA MAPPER_REG_IRQ_CTRLSTA MAPPER_REG_IRQ_ACK.ENDM;====================================================================================================
MACRO_ACK_IRQ .MACROSTA MAPPER_REG_IRQ_DISABLESTA MAPPER_REG_IRQ_ENABLE.ENDM;====================================================================================================
MACRO_IRQ_OPERATE .MACRO;==================================================
;IRQ滚动模式常量
IRQ_SCROLL_MODE_ZERO = 0 ;不滚动
IRQ_SCROLL_MODE_LEFT = 1 ;向左滚动
IRQ_SCROLL_MODE_RIGHT = 2 ;向右滚动;IRQ扫描线数据
IRQ_Scanline_Data.DB IRQ_SCANLINE_1.DB IRQ_SCANLINE_2.DB IRQ_SCANLINE_3.DW 00 ;关闭IRQ;IRQ滚动控制模式
IRQ_Scanline_Mode.DB IRQ_SCROLL_MODE_RIGHT.DB IRQ_SCROLL_MODE_LEFT.DB IRQ_SCROLL_MODE_RIGHT.DB IRQ_SCROLL_MODE_LEFT;==================================================
;;IRQ滚动控制
IRQ_Set_ScrollLDX IRQ_Process_IndexLDA IRQ_Scanline_Mode,XCMP #IRQ_SCROLL_MODE_LEFTBEQ IRQ_Set_Scroll_LeftCMP #IRQ_SCROLL_MODE_RIGHTBEQ IRQ_Set_Scroll_Right
IRQ_Set_Scroll_Zero;不滚动LDA #$00STA PPU_SCROLLSTA PPU_SCROLLRTS
IRQ_Set_Scroll_Left;向左滚动LDA Scroll_HSTA PPU_SCROLLSTA PPU_SCROLLRTS
IRQ_Set_Scroll_Right;向右滚动LDA #$00SECSBC Scroll_HSTA PPU_SCROLLSTA PPU_SCROLLRTS;==================================================
;;IRQ滚动控制
IRQ_Set_CtrlLDX IRQ_Process_IndexLDA IRQ_Scanline_Data,XBNE IRQ_Process_Latch
IRQ_Process_Disable;禁用IRQSTA MAPPER_REG_IRQ_DISABLESTA IRQ_Process_IndexRTS
IRQ_Process_Latch;设置下次 IRQ 触发扫描线STA MAPPER_REG_IRQ_LATCHINC IRQ_Process_IndexRTS;==================================================
;IRQ处理
IRQ_Process_By_IndexJSR IRQ_Set_ScrollJSR IRQ_Set_Ctrl
IRQ_Process_EndRTS.ENDM
公用配置
[FC][Mapper][Config].asm
;[FC][Mapper][IRQ]
;FlameCyclone 20231201.INCLUDE "[FC][Mapper][MMC3].asm";.INCLUDE "[FC][Mapper][MMC5].asm";.INCLUDE "[FC][Mapper][Namco163].asm";.INCLUDE "[FC][Mapper][VRC2&4].asm";.INCLUDE "[FC][Mapper][VRC3].asm";.INCLUDE "[FC][Mapper][VRC6].asm";.INCLUDE "[FC][Mapper][VRC7].asm";.INCLUDE "[FC][Mapper][FME7].asm";.INCLUDE "[FC][Mapper][Mapper18].asm";.INCLUDE "[FC][Mapper][Mapper64].asm";文件头配置
NES_16KB_PRG_SIZE = 2
NES_8KB_CHR_SIZE = 1
BANK_DATA_MASK = NES_16KB_PRG_SIZE * 2 - 1 ;bank号掩码
RESET_BANK = NES_16KB_PRG_SIZE * 2 - 1
;======================================================================
PRG_DATA_BANK_C000 = NES_16KB_PRG_SIZE * 2 - 2
PRG_DATA_BANK_E000 = NES_16KB_PRG_SIZE * 2 - 1;文件头
;======================================================================.INESPRG NES_16KB_PRG_SIZE ;16KB PRG 数量.INESCHR NES_8KB_CHR_SIZE ;8KB CHR 数量.INESMAP MAPPER_NUMBER.INESMIR 0 ;命名表镜像 0水平 1垂直;==================================================
;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 + 选通;--------------------------------------------------
PROGRAM_BANK = PRG_DATA_BANK_E000
PROGRAM_ADDR = $E000;==================================================
;零页内存地址配置
Use_Ram_Begin = $80.RSSET Use_Ram_Begin
PPU_Ctrl_Buf .RS 1
PPU_Msak_Buf .RS 1
PPU_Scroll_H .RS 1
PPU_Scroll_V .RS 1
FC_Data_L .RS 1
FC_Data_H .RS 1
FC_Data_Buf .RS 1
FC_Data_Index .RS 1;==================================================
GAMEPAD_MERGE_FLAG = $04Gamepad_Keep .RS 2
Gamepad_Once .RS 2
Gamepad_Temp .RS 2
Gamepad_0_State .RS 1
Gamepad_1_State .RS 1
Gamepad_0_Value .RS 1
Gamepad_1_Value .RS 1
Gamepad_Port_Value .RS 1
Gamepad_Merge .RS 1;==================================================
Scroll_H .RS 1
Scroll_V .RS 1
IRQ_Process_Index .RS 1;==================================================
Prg_Bank_8000 .RS 1
Prg_Bank_A000 .RS 1
Prg_Bank_C000 .RS 1
Prg_Bank_E000 .RS 1
Prg_Bank_8000_Bak .RS 1
Prg_Bank_A000_Bak .RS 1
Prg_Bank_C000_Bak .RS 1
Prg_Bank_E000_Bak .RS 1;==================================================
Prg_Bank_A_Bak .RS 1;==================================================
公用主程序代码
[FC][Mapper][IRQ].asm
.INCLUDE "[FC][Mapper][Config].asm";CHR图形数据
;======================================================================.BANK NES_16KB_PRG_SIZE * 2.INCBIN "chr_bank/chr_data.chr".BANK PROGRAM_BANK & BANK_DATA_MASK.ORG PROGRAM_ADDR;--------------------------------------------------
Attributes_Data
;命名表属性.DB $00,$00,$00,$00,$00,$00,$00,$00,$55,$55,$55,$55,$55,$55,$55,$55.DB $55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55.DB $55,$F5,$F5,$F5,$F5,$F5,$F5,$55,$55,$FF,$FF,$FF,$FF,$FF,$FF,$75.DB $A5,$A5,$A5,$A5,$A5,$A5,$A5,$A5,$AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA
;--------------------------------------------------
;调色板数据
Palette_Data.DB $0F,$27,$20,$0F,$0F,$23,$20,$0F,$0F,$21,$20,$0F,$0F,$24,$20,$0F.DB $0F,$21,$24,$25,$0F,$24,$20,$0F,$0F,$24,$20,$0F,$0F,$24,$20,$0F;--------------------------------------------------
;命名表文本索引
Name_Table_Text_Index.DW .Name_Table_Text_Data_1.DW .Name_Table_Text_Data_2.DW .Name_Table_Text_Data_3.DW .Name_Table_Text_Data_4.DW .Name_Table_Text_Data_5.DW $00;结束标记.Name_Table_Text_Data_1.DB $20,$48.STR "MAPPER IRQ TEST".Name_Table_Text_Data_2.DB $22,$28.STR "SCROLL RIGHT".Name_Table_Text_Data_3.DB $22,$68.STR "SCROLL LEFT".Name_Table_Text_Data_4.DB $23,$26.STR "MADE BY FLAMECYCLONE".Name_Table_Text_Data_5.DB $23,$6B.STR "2023.12.01";==================================================
;命名表初始化
Init_Name_TableLDA #$20STA PPU_ADDRESSLDA #$00STA PPU_ADDRESSLDA #$00LDY #$00LDX #$10
Init_Name_Table_WriteSTA PPU_DATAINYBNE Init_Name_Table_WriteDEXBNE Init_Name_Table_WriteRTS;==================================================
;调色板初始化
Init_PaletteBIT PPU_STATUSLDA #$3FSTA PPU_ADDRESSLDA #$00STA PPU_ADDRESSLDX #$00
Init_Palette_WriteLDA Palette_Data,XSTA PPU_DATAINXCPX #$20BCC Init_Palette_WriteRTS;==================================================
;设置命名表属性
Init_NameTable_AttributesBIT PPU_STATUSLDA #$23STA PPU_ADDRESSLDA #$C0STA PPU_ADDRESSLDX #$00
Init_NameTable_Attributes_WriteLDA Attributes_Data,XSTA PPU_DATAINXCPX #$40BCC Init_NameTable_Attributes_WriteRTS;==================================================
;初始化命名表文本
Init_Name_Table_TextBIT PPU_STATUSBIT $FFFFLDA #$00STA <FC_Data_Index;检查需要写入的文本条目
.Write_Text_BeginLDA <FC_Data_IndexASL ATAYLDA Name_Table_Text_Index,YSTA <FC_Data_LINYLDA Name_Table_Text_Index,YSTA <FC_Data_H;没有文本索引则结束LDA <FC_Data_LORA <FC_Data_HBEQ .EndJSR .Write_Text_DataINC <FC_Data_IndexJMP .Write_Text_Begin.EndRTS;读取文本数据位置和长度
.Write_Text_DataLDY #$00LDA [FC_Data_L],YSTA PPU_ADDRESSINYLDA [FC_Data_L],YSTA PPU_ADDRESSINYLDA [FC_Data_L],YTAXINY
;写入PPU数据
.Write_PPU_DataLDA [FC_Data_L],YSTA PPU_DATAINYDEXBNE .Write_PPU_DataRTS;==============================
Init_OAM_Ram;初始化精灵内存LDX #$00LDA #$00STA PPU_OAM_ADDRLDA #$F8
Init_OAM_Ram_WriteSTA PPU_OAM_DATAINXBNE Init_OAM_Ram_WriteRTSGamepadProcess;手柄处理JSR GamepadDatacanLDA <Gamepad_0_ValueSTA <Gamepad_0_StateLDA <Gamepad_1_ValueSTA <Gamepad_1_StateJSR GamepadDatacanLDX #$01
GamepadMergeCheck;合并手柄输入检查LDA <Gamepad_0_Value,XCMP <Gamepad_0_State,XBEQ GamepadMergeInputLDA <Gamepad_Temp,XSTA <Gamepad_0_Value,X
GamepadMergeInput;合并手柄输入DEXBPL GamepadMergeCheckLDA <Gamepad_MergeAND #GAMEPAD_MERGE_FLAGBNE GamepadStateProcessLDA <Gamepad_0_ValueORA <Gamepad_1_ValueSTA <Gamepad_0_Value
GamepadStateProcess;手柄状态处理LDX #$01
GamepadStateSave;手柄状态保存LDA <Gamepad_0_Value,XTAYEOR <Gamepad_Temp,XAND <Gamepad_0_Value,XSTA <Gamepad_Once,XSTY <Gamepad_Keep,XSTY <Gamepad_Temp,XDEXBPL GamepadStateSaveRTSGamepadDatacan;手柄数据扫描LDX #$01STX $4016DEXSTX $4016LDY #$08
GamepadPortScan;手柄端口扫描LDA $4016STA <Gamepad_Port_ValueLSR AORA <Gamepad_Port_ValueLSR AROL <Gamepad_0_ValueLDA $4017STA <Gamepad_Port_ValueLSR AORA <Gamepad_Port_ValueLSR AROL <Gamepad_1_ValueDEYBNE GamepadPortScanRTS;==================================================
;PPU处理, 这里仅重置PPU滚动
PPU_ProcessLDA #$00STA PPU_MASKBIT PPU_STATUSLDA #$20STA PPU_ADDRESSLDA #$00STA PPU_ADDRESSSTA PPU_SCROLLSTA PPU_SCROLLLDA PPU_Msak_BufSTA PPU_MASKRTS;==============================
Time_For_Vblank;延时等待LDA PPU_STATUSBPL Time_For_VblankRTS;==================================================
;重置中断处理
ResetProgramSEICLDLDA #$00STA PPU_CTRLSTA PPU_MASKSTA PPU_STATUSSTA JOY2_FRAMESTA APU_STATUSLDA #$C0STA JOY2_FRAME;等待vblankLDX #$02
Vblank_Wait_1BIT PPU_STATUSBPL Vblank_Wait_1
Vblank_Wait_2BIT PPU_STATUSBMI Vblank_Wait_2DEXBNE Vblank_Wait_1LDX #$FFTXS;初始化MapperMACRO_MAPPER_INIT;==============================
;RAM初始化
Nes_Ram_InitLDY #$00LDX #$08LDA #$00STA <$00STA <$01
Nes_Ram_Init_WriteSTA [$00],YINYBNE Nes_Ram_Init_WriteINC <$01DEXBNE Nes_Ram_Init_Write;初始化命名表JSR Init_Name_Table;初始化调色板JSR Init_Palette;初始化命名表属性JSR Init_NameTable_Attributes;初始化精灵内存JSR Init_OAM_Ram;在屏幕上写点东西JSR Init_Name_Table_TextJSR Time_For_Vblank;开启PPU控制LDA #$A8STA PPU_Ctrl_BufSTA PPU_CTRL;开启PPU显示LDA #$1ESTA PPU_Msak_BufCLIJMP Loop;==============================
;死循环, 等待NMI中断
LoopJMP Loop;IRQ宏代码定义MACRO_IRQ_OPERATE;==================================================
;NMI中断处理
NmiProgramPHATXAPHATYAPHA;读取清除Vblank标志, 防止重复进入BIT PPU_STATUS;水平滚动值增加INC Scroll_H;启动IRQMACRO_TRIGGER_FIRST_IRQ;重置IRQ索引LDA #$00STA <IRQ_Process_Index;关闭PPU控制LDA #$00STA PPU_CTRL;处理PPUJSR PPU_Process;开启PPU控制LDA PPU_Ctrl_BufSTA PPU_CTRL;手柄处理JSR GamepadProcessPLATAYPLATAXPLARTI;==================================================
;IRQ中断处理
IrqProgramPHATXAPHATYAPHA;IRQ确认MACRO_ACK_IRQ;IRQ处理JSR IRQ_Process_By_IndexIrqProgramEndPLATAYPLATAXPLARTI;==================================================
;中断表.ORG $FFFA.WORD NmiProgram.WORD ResetProgram.WORD IrqProgram
gitee代码库:
FlameCyclone/FC IRQ (gitee.com)