535 lines
11 KiB
C
535 lines
11 KiB
C
#include "main.h"
|
||
#include "stm32f4xx.h"
|
||
#include "nes_main.h"
|
||
#include "nes_ppu.h"
|
||
#include "nes_mapper.h"
|
||
#include "nes_apu.h"
|
||
#include "mymem.h"
|
||
#include "dac.h"
|
||
|
||
#include "ff.h"
|
||
#include "string.h"
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
//本程序移植自网友ye781205的NES模拟器工程
|
||
//ALIENTEK STM32F407开发板
|
||
//NES主函数 代码
|
||
//正点原子@ALIENTEK
|
||
//技术论坛:www.openedv.com
|
||
//创建日期:2014/7/1
|
||
//版本:V1.0
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
u8 nes_frame_cnt; //nes帧计数器
|
||
int MapperNo; //map编号
|
||
int NES_scanline; //nes扫描线
|
||
int VROM_1K_SIZE;
|
||
int VROM_8K_SIZE;
|
||
|
||
u8 PADdata; //手柄1键值 [7:0]右7 左6 下5 上4 Start3 Select2 B1 A0
|
||
u8 PADdata1; //手柄2键值 [7:0]右7 左6 下5 上4 Start3 Select2 B1 A0
|
||
u8 *NES_RAM; //保持1024字节对齐
|
||
u8 *NES_SRAM;
|
||
NES_header *RomHeader; //rom文件头
|
||
MAPPER *NES_Mapper;
|
||
MapperCommRes *MAPx;
|
||
|
||
|
||
u8* spr_ram; //精灵RAM,256字节
|
||
ppu_data* ppu; //ppu指针
|
||
u8* VROM_banks;
|
||
u8* VROM_tiles;
|
||
|
||
apu_t *apu; //apu指针
|
||
u16 *wave_buffers;
|
||
u16 *i2sbuf1; //音频缓冲帧,占用内存数 367*4 字节@22050Hz
|
||
u16 *i2sbuf2; //音频缓冲帧,占用内存数 367*4 字节@22050Hz
|
||
|
||
u8* romfile; //nes文件指针,指向整个nes文件的起始地址.
|
||
//////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
//记录nes是否需要跳出
|
||
static u8 g_nes_break=0;
|
||
|
||
|
||
|
||
|
||
//加载ROM
|
||
//返回值:0,成功
|
||
// 1,内存错误
|
||
// 3,map错误
|
||
u8 nes_load_rom(void)
|
||
{
|
||
u8* p;
|
||
u8 i;
|
||
u8 res=0;
|
||
p=(u8*)romfile;
|
||
if(strncmp((char*)p,"NES",3)==0)
|
||
{
|
||
RomHeader->ctrl_z=p[3];
|
||
RomHeader->num_16k_rom_banks=p[4];
|
||
RomHeader->num_8k_vrom_banks=p[5];
|
||
RomHeader->flags_1=p[6];
|
||
RomHeader->flags_2=p[7];
|
||
if(RomHeader->flags_1&0x04)p+=512; //有512字节的trainer:
|
||
if(RomHeader->num_8k_vrom_banks>0) //存在VROM,进行预解码
|
||
{
|
||
VROM_banks=p+16+(RomHeader->num_16k_rom_banks*0x4000);
|
||
#if NES_RAM_SPEED==1 //1:内存占用小 0:速度快
|
||
VROM_tiles=VROM_banks;
|
||
#else
|
||
VROM_tiles=mymalloc(RomHeader->num_8k_vrom_banks*8*1024);//这里可能申请多达1MB内存!!!
|
||
if(VROM_tiles==0)VROM_tiles=VROM_banks;//内存不够用的情况下,尝试VROM_titles与VROM_banks共用内存
|
||
compile(RomHeader->num_8k_vrom_banks*8*1024/16,VROM_banks,VROM_tiles);
|
||
#endif
|
||
}else
|
||
{
|
||
VROM_banks=mymalloc(8*1024);
|
||
VROM_tiles=mymalloc(8*1024);
|
||
if(!VROM_banks||!VROM_tiles)res=1;
|
||
}
|
||
VROM_1K_SIZE = RomHeader->num_8k_vrom_banks * 8;
|
||
VROM_8K_SIZE = RomHeader->num_8k_vrom_banks;
|
||
MapperNo=(RomHeader->flags_1>>4)|(RomHeader->flags_2&0xf0);
|
||
if(RomHeader->flags_2 & 0x0E)MapperNo=RomHeader->flags_1>>4;//忽略高四位,如果头看起来很糟糕
|
||
//printf("use map:%d\r\n",MapperNo);
|
||
for(i=0;i<255;i++) // 查找支持的Mapper号
|
||
{
|
||
if (MapTab[i]==MapperNo)break;
|
||
if (MapTab[i]==-1)res=3;
|
||
}
|
||
if(res==0)
|
||
{
|
||
switch(MapperNo)
|
||
{
|
||
case 1:
|
||
MAP1=mymalloc(sizeof(Mapper1Res));
|
||
if(!MAP1)res=1;
|
||
break;
|
||
case 4:
|
||
case 6:
|
||
case 16:
|
||
case 17:
|
||
case 18:
|
||
case 19:
|
||
case 21:
|
||
case 23:
|
||
case 24:
|
||
case 25:
|
||
case 64:
|
||
case 65:
|
||
case 67:
|
||
case 69:
|
||
case 85:
|
||
case 189:
|
||
MAPx=mymalloc(sizeof(MapperCommRes));
|
||
if(!MAPx)res=1;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return res; //返回执行结果
|
||
}
|
||
//变量NES_RAM申请内存时的辅助指针
|
||
void *nes_ram_alignment;
|
||
//释放内存
|
||
void nes_sram_free(void)
|
||
{
|
||
//myfree(NES_RAM);
|
||
myfree(nes_ram_alignment);
|
||
myfree(NES_SRAM);
|
||
myfree(RomHeader);
|
||
myfree(NES_Mapper);
|
||
myfree(spr_ram);
|
||
myfree(ppu);
|
||
myfree(apu);
|
||
myfree(wave_buffers);
|
||
myfree(i2sbuf1);
|
||
myfree(i2sbuf2);
|
||
myfree(romfile);
|
||
if((VROM_tiles!=VROM_banks)&&VROM_banks&&VROM_tiles)//如果分别为VROM_banks和VROM_tiles申请了内存,则释放
|
||
{
|
||
myfree(VROM_banks);
|
||
myfree(VROM_tiles);
|
||
}
|
||
switch (MapperNo)//释放map内存
|
||
{
|
||
case 1: //释放内存
|
||
myfree(MAP1);
|
||
break;
|
||
case 4:
|
||
case 6:
|
||
case 16:
|
||
case 17:
|
||
case 18:
|
||
case 19:
|
||
case 21:
|
||
case 23:
|
||
case 24:
|
||
case 25:
|
||
case 64:
|
||
case 65:
|
||
case 67:
|
||
case 69:
|
||
case 85:
|
||
case 189:
|
||
myfree(MAPx);break; //释放内存
|
||
default:break;
|
||
}
|
||
NES_RAM=0;
|
||
NES_SRAM=0;
|
||
RomHeader=0;
|
||
NES_Mapper=0;
|
||
spr_ram=0;
|
||
ppu=0;
|
||
apu=0;
|
||
wave_buffers=0;
|
||
i2sbuf1=0;
|
||
i2sbuf2=0;
|
||
romfile=0;
|
||
VROM_banks=0;
|
||
VROM_tiles=0;
|
||
MAP1=0;
|
||
MAPx=0;
|
||
}
|
||
//为NES运行申请内存
|
||
//romsize:nes文件大小
|
||
//返回值:0,申请成功
|
||
// 1,申请失败
|
||
u8 nes_sram_malloc(u32 romsize)
|
||
{
|
||
u16 i=0;
|
||
|
||
//NES_RAM需要0x800大小的内存,这里多申请0x400的内存,用于1024字节对齐
|
||
nes_ram_alignment=mymalloc(0x800+0x400);
|
||
NES_RAM=(void*)(((u32)nes_ram_alignment+0x400)&0xfffffc00);
|
||
|
||
NES_SRAM=mymalloc(0X2000);
|
||
RomHeader=mymalloc(sizeof(NES_header));
|
||
NES_Mapper=mymalloc(sizeof(MAPPER));
|
||
spr_ram=mymalloc(0X100);
|
||
ppu=mymalloc(sizeof(ppu_data));
|
||
apu=mymalloc(sizeof(apu_t)); //sizeof(apu_t)= 12588
|
||
wave_buffers=mymalloc(APU_PCMBUF_SIZE*2);
|
||
i2sbuf1=mymalloc(APU_PCMBUF_SIZE*4+10);
|
||
i2sbuf2=mymalloc(APU_PCMBUF_SIZE*4+10);
|
||
romfile=mymalloc(romsize); //申请游戏rom空间,等于nes文件大小
|
||
if(i==64||!NES_RAM||!NES_SRAM||!RomHeader||!NES_Mapper||!spr_ram||!ppu||!apu||!wave_buffers||!i2sbuf1||!i2sbuf2||!romfile)
|
||
{
|
||
nes_sram_free();
|
||
return 1;
|
||
}
|
||
memset(NES_SRAM,0,0X2000); //清零
|
||
memset(RomHeader,0,sizeof(NES_header)); //清零
|
||
memset(NES_Mapper,0,sizeof(MAPPER)); //清零
|
||
memset(spr_ram,0,0X100); //清零
|
||
memset(ppu,0,sizeof(ppu_data)); //清零
|
||
memset(apu,0,sizeof(apu_t)); //清零
|
||
memset(wave_buffers,0,APU_PCMBUF_SIZE*2);//清零
|
||
memset(i2sbuf1,0,APU_PCMBUF_SIZE*4+10); //清零
|
||
memset(i2sbuf2,0,APU_PCMBUF_SIZE*4+10); //清零
|
||
memset(romfile,0,romsize); //清零
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
int nes_xoff=0; //显示在x轴方向的偏移量(实际显示宽度=256-2*nes_xoff)
|
||
int nes_yoff=0;
|
||
|
||
|
||
|
||
//开始nes游戏
|
||
//pname:nes游戏路径
|
||
//返回值:
|
||
//0,正常退出
|
||
//1,内存错误
|
||
//2,文件错误
|
||
//3,不支持的map
|
||
u8 nes_load(u8* pname,void *lcd_addr,int x,int y)
|
||
{
|
||
nes_xoff=x;
|
||
nes_yoff=y;
|
||
FIL *file;
|
||
FILINFO *file_info;
|
||
UINT br;
|
||
u8 res=0;
|
||
// app_wm8978_volset(wm8978set.mvol);
|
||
// WM8978_ADDA_Cfg(1,0); //开启DAC
|
||
// WM8978_Input_Cfg(0,0,0);//关闭输入通道
|
||
// WM8978_Output_Cfg(1,0); //开启DAC输出
|
||
g_nes_break=0;
|
||
file=mymalloc(sizeof(FIL));
|
||
file_info= mymalloc(sizeof(FILINFO));
|
||
if(file==0)return 1; //内存申请失败.
|
||
|
||
res=f_stat((char*)pname,file_info);
|
||
if(res!=FR_OK) //打开文件失败
|
||
{
|
||
myfree(file);
|
||
myfree(file_info);
|
||
return 2;
|
||
}
|
||
res=nes_sram_malloc(file_info->fsize); //申请内存
|
||
if(res==0)
|
||
{
|
||
f_open(file,(char*)pname,FA_READ);
|
||
f_read(file,romfile,file_info->fsize,&br); //读取nes文件
|
||
res=nes_load_rom(); //加载ROM
|
||
if(res==0)
|
||
{
|
||
Mapper_Init(); //map初始化
|
||
cpu6502_init(); //初始化6502,并复位
|
||
PPU_reset(lcd_addr); //ppu复位
|
||
apu_init(); //apu初始化
|
||
nes_sound_open(0,APU_SAMPLE_RATE); //初始化播放设备
|
||
nes_emulate_frame(); //进入NES模拟器主循环
|
||
nes_sound_close(); //关闭声音输出
|
||
}
|
||
}
|
||
f_close(file);
|
||
myfree(file);//释放内存
|
||
myfree(file_info);
|
||
nes_sram_free(); //释放内存
|
||
return res;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//运行已经加载在内存中的游戏
|
||
u8 nes_start(u8* file,int fileSize,void *lcd_addr,int x,int y)
|
||
{
|
||
nes_xoff=x;
|
||
nes_yoff=y;
|
||
u8 res=0;
|
||
g_nes_break=0;
|
||
res=nes_sram_malloc(fileSize); //申请内存
|
||
|
||
if(res==0)
|
||
{
|
||
mymemcpy (romfile,file,fileSize);//复制一份在运行空间
|
||
res=nes_load_rom(); //加载ROM
|
||
if(res==0)
|
||
{
|
||
Mapper_Init(); //map初始化
|
||
cpu6502_init(); //初始化6502,并复位
|
||
PPU_reset(lcd_addr); //ppu复位
|
||
apu_init(); //apu初始化
|
||
nes_sound_open(0,APU_SAMPLE_RATE); //初始化播放设备
|
||
nes_emulate_frame(); //进入NES模拟器主循环
|
||
nes_sound_close(); //关闭声音输出
|
||
}
|
||
}
|
||
nes_sram_free(); //释放内存
|
||
return res;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//设置游戏显示窗口
|
||
void nes_set_window(void)
|
||
{
|
||
}
|
||
//读取游戏手柄数据
|
||
void nes_get_gamepadval(void)
|
||
{
|
||
#include "usart.h"
|
||
PADdata=USART3_GetKey();
|
||
PADdata1=0;
|
||
}
|
||
//nes模拟器主循环
|
||
void nes_emulate_frame(void)
|
||
{
|
||
u8 nes_frame;
|
||
//TIM3_Int_Init(10000-1,8400-1);//启动TIM3 ,1s中断一次
|
||
nes_set_window();//设置窗口
|
||
while(1)
|
||
{
|
||
// LINES 0-239
|
||
PPU_start_frame();
|
||
//LCD_SwitchLayerBuff();//切换帧
|
||
for(NES_scanline = 0; NES_scanline< 240; NES_scanline++)
|
||
{
|
||
run6502(113*256);
|
||
NES_Mapper->HSync(NES_scanline);
|
||
//扫描一行
|
||
if(nes_frame==0)scanline_draw(NES_scanline);
|
||
else do_scanline_and_dont_draw(NES_scanline);
|
||
}
|
||
//LCD_ExitLayerBuff ();
|
||
|
||
NES_scanline=240;
|
||
run6502(113*256);//运行1线
|
||
NES_Mapper->HSync(NES_scanline);
|
||
start_vblank();
|
||
if(NMI_enabled())
|
||
{
|
||
cpunmi=1;
|
||
run6502(7*256);//运行中断
|
||
}
|
||
NES_Mapper->VSync();
|
||
// LINES 242-261
|
||
for(NES_scanline=241;NES_scanline<262;NES_scanline++)
|
||
{
|
||
run6502(113*256);
|
||
NES_Mapper->HSync(NES_scanline);
|
||
}
|
||
end_vblank();
|
||
nes_get_gamepadval();//每3帧查询一次USB
|
||
apu_soundoutput();//输出游戏声音
|
||
nes_frame_cnt++;
|
||
nes_frame++;
|
||
if(nes_frame>NES_SKIP_FRAME)nes_frame=0;//跳帧
|
||
u8 nesBreak (void);
|
||
if (nesBreak()) break;
|
||
if (Touch_GetState()->x[1]!=0) break;
|
||
}
|
||
//TIM3->CR1&=~(1<<0);//关闭定时器3
|
||
}
|
||
|
||
|
||
|
||
|
||
u8 nesBreak (void)
|
||
{
|
||
u8 t=0;
|
||
if ((PADdata&0x0c)==0x0c)
|
||
{
|
||
g_nes_break=0x0c;
|
||
}
|
||
if ((g_nes_break)&&(t=USART3_GetKeyPressed(),t&0x0c))
|
||
{
|
||
g_nes_break&=~t;
|
||
if (g_nes_break==0)
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//在6502.s里面被调用
|
||
void debug_6502(u16 reg0,u8 reg1)
|
||
{
|
||
//printf("6502 error:%x,%d\r\n",reg0,reg1);
|
||
}
|
||
|
||
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
//nes,音频输出支持部分
|
||
|
||
|
||
|
||
static vu8 g_buff_Invalid=0; //数据无效时要重新填充数据
|
||
static vu8 g_buff_useing; //当前DMA在使用哪个缓冲区
|
||
static DAC_UserStruct g_dac;
|
||
|
||
|
||
|
||
static void dma_tx_callback(DAC_UserStruct *dac)
|
||
{
|
||
u16 i;
|
||
g_buff_useing=dac->buff_useing;
|
||
g_buff_Invalid=1;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
//NES打开音频输出
|
||
int nes_sound_open(int samples_per_sync,int sample_rate)
|
||
{
|
||
|
||
//初始化DAC
|
||
g_dac.buff1=(u32 *)i2sbuf1;
|
||
g_dac.buff2=(u32 *)i2sbuf2;
|
||
g_dac.buff_size=APU_PCMBUF_SIZE*4;
|
||
g_dac.rate=RATE22050;
|
||
g_dac.call_back=dma_tx_callback;
|
||
DAC_NormalInit (&g_dac);
|
||
return 1;
|
||
}
|
||
//NES关闭音频输出
|
||
void nes_sound_close(void)
|
||
{
|
||
DAC_NormalDeInit (&g_dac);
|
||
}
|
||
//NES音频输出到I2S缓存
|
||
void nes_apu_fill_buffer(int samples,u16* wavebuf)
|
||
{
|
||
|
||
if (DAC_GetDacHander()!=&g_dac) return;
|
||
u16 i;
|
||
u16 *p;
|
||
while(g_buff_Invalid==0)//等待传输完成
|
||
{
|
||
rt_thread_delay(5);//
|
||
};
|
||
if(g_buff_useing==0)
|
||
{
|
||
p=(u16*)i2sbuf2;
|
||
}else
|
||
{
|
||
p=(u16*)i2sbuf1;
|
||
}
|
||
{
|
||
for(i=0;i<APU_PCMBUF_SIZE;i++)
|
||
{
|
||
p[2*i]=((wavebuf[i]+0x8000)>>4);
|
||
p[2*i+1]=p[2*i];
|
||
}
|
||
}
|
||
//填充了数据之后设置为有效
|
||
g_buff_Invalid=0;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|