对比了很多种,开源的 NES 模拟器 VirtuaNES , nestopia , FakeNES , FCEUX , InfoNES , LiteNES
最后决定使用 LiteNES 进行移值,它是由 mynes 移值而来。LiteNES 对 mynes 代码进行整理兼容了 C99 标准,编译时无警告。
https://github.com/NJUOS/LiteNES
https://github.com/yaglo/mynes
LiteNES , mynes 基于 Allegro ,Allegro 是一种提供底层画图,输入,定时器等支持的库。
LiteNES 全部抽象提取代码到 一个 hal.c 文件里面,修改移值十分方便。下面是修改后的样子,替换这个文件。
在修改下 Makefile 中的 gcc 改为 arm-linux-gcc 编译 即可在板子上出现 Game 画面了(经过测试,发现支持的游戏不多,有的载加不出来)
NES 移值第1篇,仅能出现画面。以后会更新添加声音,多线程,输入等。现在只为能显示出画面。
hal.c :
1 /* 2 This file present all abstraction needed to port LiteNES. 3 (The current working implementation uses allegro library.) 4 5 To port this project, replace the following functions by your own: 6 1) nes_hal_init() 7 Do essential initialization work, including starting a FPS HZ timer. 8 9 2) nes_set_bg_color(c) 10 Set the back ground color to be the NES internal color code c. 11 12 3) nes_flush_buf(*buf) 13 Flush the entire pixel buf's data to frame buffer. 14 15 4) nes_flip_display() 16 Fill the screen with previously set background color, and 17 display all contents in the frame buffer. 18 19 5) wait_for_frame() 20 Implement it to make the following code is executed FPS times a second: 21 while (1) { 22 wait_for_frame(); 23 do_something(); 24 } 25 26 6) int nes_key_state(int b) 27 Query button b's state (1 to be pressed, otherwise 0). 28 The correspondence of b and the buttons: 29 0 - Power 30 1 - A 31 2 - B 32 3 - SELECT 33 4 - START 34 5 - UP 35 6 - DOWN 36 7 - LEFT 37 8 - RIGHT 38 */ 39 #include "hal.h" 40 #include "fce.h" 41 #include "common.h" 42 43 /** 44 * allegro API 不明白的看文档 45 * https://www.allegro.cc/manual/5/index.html 46 */ 47 /* lcd 操作相关 头文件 */ 48 #include <sys/types.h> 49 #include <sys/stat.h> 50 #include <fcntl.h> 51 #include <linux/fb.h> 52 #include <sys/ioctl.h> 53 #include <unistd.h> 54 #include <string.h> 55 #include <sys/mman.h> 56 57 static int fb_fd; 58 static unsigned char *fb_mem; 59 static int px_width; 60 static int line_width; 61 static int screen_width; 62 static struct fb_var_screeninfo var; 63 64 static int lcd_fb_display_px(int color, int x, int y) 65 { 66 unsigned char *pen8; 67 unsigned short *pen16; 68 69 unsigned char r,g,b; 70 71 pen8 = (unsigned char *)(fb_mem + y*line_width + x*px_width); 72 pen16 = (unsigned short *)pen8; 73 74 //合并为 565 格式 16bbp 75 r = (color>>16) & 0xff; 76 g = (color>>8) & 0xff; 77 b = (color>>0) & 0xff; 78 *pen16 = (r>>3)<<11 | (g>>2)<<5 | (b>>3); 79 80 return 0; 81 } 82 83 //调色板转16进行32位颜色 84 static int pal2color(pal pal) 85 { 86 int color = 0; 87 color = pal.r << 16 | pal.g <<8 | pal.b; 88 return color; 89 } 90 91 static int lcd_fb_init() 92 { 93 //如果使用 mmap 打开方式 必须是 读写方式 94 fb_fd = open("/dev/fb0", O_RDWR); 95 if(-1 == fb_fd) 96 { 97 printf("cat't open /dev/fb0 "); 98 return -1; 99 } 100 //获取屏幕参数 101 if(-1 == ioctl(fb_fd, FBIOGET_VSCREENINFO, &var)) 102 { 103 close(fb_fd); 104 printf("cat't ioctl /dev/fb0 "); 105 return -1; 106 } 107 //计算参数 108 px_width = var.bits_per_pixel / 8; 109 line_width = var.xres * px_width; 110 screen_width = var.yres * line_width; 111 112 fb_mem = (unsigned char *)mmap(NULL, screen_width, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0); 113 if(fb_mem == (void *)-1) 114 { 115 close(fb_fd); 116 printf("cat't mmap /dev/fb0 "); 117 return -1; 118 } 119 //清屏 120 memset(fb_mem, 0 , screen_width); 121 return 0; 122 } 123 124 /* Wait until next allegro timer event is fired. */ 125 void wait_for_frame() 126 { 127 //休眠 FPS = 60 * 1000 毫秒 128 usleep(1/FPS*1000); 129 } 130 131 /* Set background color. RGB value of c is defined in fce.h */ 132 void nes_set_bg_color(int c) 133 { 134 //画背景颜色 135 int i,j; 136 for(i=0; i<SCREEN_WIDTH; i++) 137 { 138 for(j=0; j<SCREEN_HEIGHT; j++) 139 { 140 lcd_fb_display_px(pal2color(palette[c]), i, j); 141 } 142 } 143 } 144 145 /* Flush the pixel buffer */ 146 void nes_flush_buf(PixelBuf *buf) 147 { 148 Pixel *p; 149 int i,x,y,color; 150 for (i = 0; i < buf->size; i++) 151 { 152 p = &buf->buf[i]; 153 x = p->x; 154 y = p->y; 155 156 color = pal2color(palette[p->c]); 157 lcd_fb_display_px(color, x, y); 158 lcd_fb_display_px(color, x+1, y); 159 lcd_fb_display_px(color, x, y+1); 160 lcd_fb_display_px(color, x+1, y+1); 161 } 162 } 163 164 /* Initialization: 165 (1) start a 1/FPS Hz timer. 166 (2) register fce_timer handle on each timer event */ 167 void nes_hal_init() 168 { 169 /** 170 * 需要完成的事情 171 * 1,初始化 lcd 172 * 2,初始化 定时器 先做简单的直接用系统延时 173 */ 174 if(-1 == lcd_fb_init()) 175 { 176 printf("lcd fb init error "); 177 return ; 178 } 179 } 180 181 /* Update screen at FPS rate by allegro's drawing function. 182 Timer ensures this function is called FPS times a second. */ 183 void nes_flip_display() 184 { 185 //设置64种颜色值 不必设置 186 } 187 188 /* Query a button's state. 189 Returns 1 if button #b is pressed. */ 190 int nes_key_state(int b) 191 { 192 switch (b) 193 { 194 case 0: // On / Off 195 return 1; 196 case 1: // A 197 return 1; 198 case 2: // B 199 return 1; 200 case 3: // SELECT 201 return 1; 202 case 4: // START 203 return 1; 204 case 5: // UP 205 return 1; 206 case 6: // DOWN 207 return 1; 208 case 7: // LEFT 209 return 1; 210 case 8: // RIGHT 211 return 1; 212 default: 213 return 1; 214 } 215 }