#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>4); p[2*i+1]=p[2*i]; } } //填充了数据之后设置为有效 g_buff_Invalid=0; }