├── .gitignore ├── .gitmodules ├── COPYING ├── common ├── cmn.c ├── cmn.h ├── host_fb.c ├── host_fb.h ├── omapfb.h ├── wiz_video.c └── wiz_video_arm.s ├── dist ├── ginge.gpe ├── ginge.ini ├── ginge.pxml ├── ginge.sh ├── ginge26.png ├── ginge32.png ├── ginge60.png ├── ginge_banner.png ├── ginge_dyn_eabi.sh ├── ginge_dyn_oabi.sh └── make_cmn.sh ├── loader ├── Makefile ├── dl.c ├── emu.c ├── emu_arm.s ├── ginge_dyn.symver ├── header.h ├── host.c ├── host_pnd.c ├── host_wiz.c ├── llibc.c ├── llibc.h ├── loader.c ├── loader_arm.s ├── loader_ia32.s ├── mmsp2-regs.h ├── override.c ├── patches.c ├── realfuncs.h ├── script_arm.lds ├── script_ia32.lds ├── syscalls.S ├── syscalls.h └── tools │ ├── mcut.c │ └── static.c ├── make_caanoo.sh ├── make_pnd.sh ├── make_wiz.sh ├── prep ├── Makefile ├── font.c └── main.c ├── readme.txt └── todo.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | cscope.out 3 | tags 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "common/warm"] 2 | path = common/warm 3 | url = git://notaz.gp2x.de/~notaz/warm.git 4 | [submodule "common/libpicofe"] 5 | path = common/libpicofe 6 | url = git://notaz.gp2x.de/~notaz/libpicofe.git 7 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | MAME License 2 | 3 | Redistribution and use of this code or any derivative works are permitted 4 | provided that the following conditions are met: 5 | 6 | * Redistributions may not be sold, nor may they be used in a commercial 7 | product or activity. 8 | 9 | * Redistributions that are modified from the original source must include the 10 | complete source code, including the source code for all components used by a 11 | binary built from the modified sources. However, as a special exception, the 12 | source code distributed need not include anything that is normally distributed 13 | (in either source or binary form) with the major components (compiler, kernel, 14 | and so on) of the operating system on which the executable runs, unless that 15 | component itself accompanies the executable. 16 | 17 | * Redistributions must reproduce the above copyright notice, this list of 18 | conditions and the following disclaimer in the documentation and/or other 19 | materials provided with the distribution. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /common/cmn.c: -------------------------------------------------------------------------------- 1 | // vim:shiftwidth=2:expandtab 2 | #ifdef LOADER 3 | #include "../loader/realfuncs.h" 4 | #endif 5 | #include 6 | #include 7 | #include 8 | 9 | #include "cmn.h" 10 | 11 | int make_local_path(char *buf, size_t size, const char *file) 12 | { 13 | ssize_t ret; 14 | char *p; 15 | 16 | p = getenv("GINGE_ROOT"); 17 | if (p != NULL) { 18 | strncpy(buf, p, size); 19 | buf[size - 1] = 0; 20 | p = buf + strlen(buf); 21 | } 22 | else { 23 | ret = readlink("/proc/self/exe", buf, size - 1); 24 | if (ret < 0) { 25 | perror("readlink"); 26 | goto err; 27 | } 28 | buf[ret] = 0; 29 | 30 | p = strrchr(buf, '/'); 31 | if (p == NULL) 32 | goto err; 33 | p++; 34 | } 35 | 36 | snprintf(p, size - (p - buf), "%s", file); 37 | 38 | return 0; 39 | 40 | err: 41 | fprintf(stderr, "ginge: can't determine root, buf: \"%s\"\n", buf); 42 | return -1; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /common/cmn.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int make_local_path(char *buf, size_t size, const char *file); 4 | -------------------------------------------------------------------------------- /common/host_fb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | #include 8 | #ifdef LOADER 9 | #include "../loader/realfuncs.h" 10 | #endif 11 | 12 | #include "host_fb.h" 13 | 14 | static void *host_screen; 15 | static int host_stride; 16 | 17 | #if defined(PND) 18 | 19 | #include "libpicofe/linux/fbdev.c" 20 | #include "omapfb.h" 21 | 22 | static struct vout_fbdev *fbdev; 23 | static unsigned short host_pal[256]; 24 | 25 | static int get_layer_size(int *x, int *y, int *w, int *h) 26 | { 27 | struct omapfb_plane_info pi; 28 | int ret; 29 | 30 | ret = ioctl(vout_fbdev_get_fd(fbdev), OMAPFB_QUERY_PLANE, &pi); 31 | if (ret != 0) { 32 | perror("OMAPFB_QUERY_PLANE"); 33 | return -1; 34 | } 35 | 36 | *x = pi.pos_x; 37 | *y = pi.pos_y; 38 | *w = pi.out_width; 39 | *h = pi.out_height; 40 | printf("layer: %d,%d %dx%d\n", *x, *y, *w, *h); 41 | 42 | return 0; 43 | } 44 | 45 | void *host_video_flip(void) 46 | { 47 | host_screen = vout_fbdev_flip(fbdev); 48 | return host_screen; 49 | } 50 | 51 | int host_video_init(int *stride, int no_dblbuf) 52 | { 53 | const char *fbdev_name; 54 | int w, h; 55 | 56 | fbdev_name = getenv("FBDEV"); 57 | if (fbdev_name == NULL) 58 | fbdev_name = "/dev/fb1"; 59 | 60 | w = h = 0; 61 | fbdev = vout_fbdev_init(fbdev_name, &w, &h, 16, no_dblbuf ? 1 : 3); 62 | if (fbdev == NULL) 63 | return -1; 64 | 65 | host_stride = w * 2; 66 | if (stride != 0) 67 | *stride = host_stride; 68 | host_video_flip(); 69 | 70 | return 0; 71 | } 72 | 73 | void host_video_finish(void) 74 | { 75 | vout_fbdev_finish(fbdev); 76 | fbdev = NULL; 77 | } 78 | 79 | void host_video_update_pal16(unsigned short *pal) 80 | { 81 | memcpy(host_pal, pal, sizeof(host_pal)); 82 | } 83 | 84 | void host_video_update_pal32(unsigned int *pal) 85 | { 86 | unsigned short *dstp = host_pal; 87 | int i; 88 | 89 | for (i = 0; i < 256; i++, pal++, dstp++) { 90 | unsigned int t = *pal; 91 | *dstp = ((t >> 8) & 0xf800) | ((t >> 5) & 0x07e0) | ((t >> 3) & 0x001f); 92 | } 93 | } 94 | 95 | void host_video_change_bpp(int bpp) 96 | { 97 | } 98 | 99 | void host_video_blit4(const unsigned char *src, int w, int h, int stride) 100 | { 101 | unsigned short *dst = host_screen; 102 | unsigned short *hpal = host_pal; 103 | int i, u; 104 | 105 | for (i = 0; i < 240; i++, dst += host_stride / 2, src += stride) { 106 | for (u = 0; i < w / 2; u++) { 107 | dst[u*2 + 0] = hpal[src[u] >> 4]; 108 | dst[u*2 + 1] = hpal[src[u] & 0x0f]; 109 | } 110 | } 111 | 112 | host_video_flip(); 113 | } 114 | 115 | void host_video_blit8(const unsigned char *src, int w, int h, int stride) 116 | { 117 | unsigned short *dst = host_screen; 118 | unsigned short *hpal = host_pal; 119 | int i, u; 120 | 121 | for (i = 0; i < 240; i++, dst += host_stride / 2, src += stride) { 122 | for (u = 0; u < w; u += 4) { 123 | dst[u + 0] = hpal[src[u + 0]]; 124 | dst[u + 1] = hpal[src[u + 1]]; 125 | dst[u + 2] = hpal[src[u + 2]]; 126 | dst[u + 3] = hpal[src[u + 3]]; 127 | } 128 | } 129 | 130 | host_video_flip(); 131 | } 132 | 133 | void host_video_blit16(const unsigned short *src, int w, int h, int stride) 134 | { 135 | unsigned short *dst = host_screen; 136 | int i; 137 | 138 | for (i = 0; i < 240; i++, dst += host_stride / 2, src += stride / 2) 139 | memcpy(dst, src, w*2); 140 | 141 | host_video_flip(); 142 | } 143 | 144 | void host_video_normalize_ts(int *x1024, int *y1024) 145 | { 146 | static int lx, ly, lw = 800, lh = 480, checked; 147 | 148 | if (!checked) { 149 | get_layer_size(&lx, &ly, &lw, &lh); 150 | checked = 1; // XXX: might change, so may need to recheck 151 | } 152 | *x1024 = (*x1024 - lx) * 1024 / lw; 153 | *y1024 = (*y1024 - ly) * 1024 / lh; 154 | } 155 | 156 | #elif defined(WIZ) 157 | 158 | #include "warm/warm.c" 159 | #include "wiz_video.c" 160 | 161 | void *host_video_flip(void) 162 | { 163 | vout_gp2x_flip(); 164 | host_screen = g_screen_ptr; 165 | return host_screen; 166 | } 167 | 168 | int host_video_init(int *stride, int no_dblbuf) 169 | { 170 | int ret; 171 | 172 | host_stride = 320 * 2; 173 | if (stride != 0) 174 | *stride = host_stride; 175 | 176 | ret = vout_gp2x_init(no_dblbuf); 177 | if (ret != 0) 178 | return ret; 179 | 180 | vout_gp2x_set_mode(16, !no_dblbuf); 181 | host_video_flip(); 182 | return 0; 183 | } 184 | 185 | void host_video_finish(void) 186 | { 187 | vout_gp2x_finish(); 188 | } 189 | 190 | void host_video_update_pal16(unsigned short *pal) 191 | { 192 | vout_gp2x_set_palette16(pal, 256); 193 | } 194 | 195 | void host_video_update_pal32(unsigned int *pal) 196 | { 197 | vout_gp2x_set_palette32(pal, 256); 198 | } 199 | 200 | void host_video_change_bpp(int bpp) 201 | { 202 | vout_gp2x_set_mode(bpp, 1); 203 | } 204 | 205 | #ifdef LOADER 206 | void host_video_blit4(const unsigned char *src, int w, int h, int stride) 207 | { 208 | memcpy(host_screen, src, 320*240/2); // FIXME 209 | host_video_flip(); 210 | } 211 | 212 | void host_video_blit8(const unsigned char *src, int w, int h, int stride) 213 | { 214 | if (probably_caanoo) { 215 | unsigned char *dst = host_screen; 216 | int i; 217 | for (i = 0; i < 240; i++, dst += 320, src += stride) 218 | memcpy(dst, src, w); 219 | } 220 | else { 221 | extern void rotated_blit8(void *dst, const void *linesx4); 222 | rotated_blit8(host_screen, src); 223 | } 224 | 225 | host_video_flip(); 226 | } 227 | 228 | void host_video_blit16(const unsigned short *src, int w, int h, int stride) 229 | { 230 | if (probably_caanoo) { 231 | unsigned short *dst = host_screen; 232 | int i; 233 | for (i = 0; i < 240; i++, dst += 320, src += stride / 2) 234 | memcpy(dst, src, w*2); 235 | } 236 | else { 237 | extern void rotated_blit16(void *dst, const void *linesx4); 238 | rotated_blit16(host_screen, src); 239 | } 240 | 241 | host_video_flip(); 242 | } 243 | #endif // LOADER 244 | 245 | void host_video_normalize_ts(int *x1024, int *y1024) 246 | { 247 | *x1024 = *x1024 * 1024 / 320; 248 | *y1024 = *y1024 * 1024 / 240; 249 | } 250 | 251 | #endif // WIZ 252 | 253 | // vim:shiftwidth=2:expandtab 254 | -------------------------------------------------------------------------------- /common/host_fb.h: -------------------------------------------------------------------------------- 1 | int host_video_init(int *stride, int no_dblbuf); 2 | void host_video_finish(void); 3 | void *host_video_flip(void); 4 | void host_video_change_bpp(int bpp); 5 | 6 | // these are mutually exclusive 7 | void host_video_update_pal16(unsigned short *pal); 8 | void host_video_update_pal32(unsigned int *pal); 9 | 10 | void host_video_blit4(const unsigned char *src, int w, int h, int stride); 11 | void host_video_blit8(const unsigned char *src, int w, int h, int stride); 12 | void host_video_blit16(const unsigned short *src, int w, int h, int stride); 13 | 14 | void host_video_normalize_ts(int *x1024, int *y1024); 15 | -------------------------------------------------------------------------------- /common/omapfb.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | **************************************************************************** 3 | *** 4 | *** This header was automatically generated from a Linux kernel header 5 | *** of the same name, to make information necessary for userspace to 6 | *** call into the kernel available to libc. It contains only constants, 7 | *** structures, and macros generated from the original header, and thus, 8 | *** contains no copyrightable information. 9 | *** 10 | *** To edit the content of this header, modify the corresponding 11 | *** source file (e.g. under external/kernel-headers/original/) then 12 | *** run bionic/libc/kernel/tools/update_all.py 13 | *** 14 | *** Any manual change here will be lost the next time this script will 15 | *** be run. You've been warned! 16 | *** 17 | **************************************************************************** 18 | ****************************************************************************/ 19 | #ifndef _UAPI__LINUX_OMAPFB_H__ 20 | #define _UAPI__LINUX_OMAPFB_H__ 21 | #include 22 | #include 23 | #include 24 | #define OMAP_IOW(num, dtype) _IOW('O', num, dtype) 25 | #define OMAP_IOR(num, dtype) _IOR('O', num, dtype) 26 | #define OMAP_IOWR(num, dtype) _IOWR('O', num, dtype) 27 | #define OMAP_IO(num) _IO('O', num) 28 | #define OMAPFB_MIRROR OMAP_IOW(31, int) 29 | #define OMAPFB_SYNC_GFX OMAP_IO(37) 30 | #define OMAPFB_VSYNC OMAP_IO(38) 31 | #define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, int) 32 | #define OMAPFB_GET_CAPS OMAP_IOR(42, struct omapfb_caps) 33 | #define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, int) 34 | #define OMAPFB_LCD_TEST OMAP_IOW(45, int) 35 | #define OMAPFB_CTRL_TEST OMAP_IOW(46, int) 36 | #define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(47, struct omapfb_update_window_old) 37 | #define OMAPFB_SET_COLOR_KEY OMAP_IOW(50, struct omapfb_color_key) 38 | #define OMAPFB_GET_COLOR_KEY OMAP_IOW(51, struct omapfb_color_key) 39 | #define OMAPFB_SETUP_PLANE OMAP_IOW(52, struct omapfb_plane_info) 40 | #define OMAPFB_QUERY_PLANE OMAP_IOW(53, struct omapfb_plane_info) 41 | #define OMAPFB_UPDATE_WINDOW OMAP_IOW(54, struct omapfb_update_window) 42 | #define OMAPFB_SETUP_MEM OMAP_IOW(55, struct omapfb_mem_info) 43 | #define OMAPFB_QUERY_MEM OMAP_IOW(56, struct omapfb_mem_info) 44 | #define OMAPFB_WAITFORVSYNC OMAP_IO(57) 45 | #define OMAPFB_MEMORY_READ OMAP_IOR(58, struct omapfb_memory_read) 46 | #define OMAPFB_GET_OVERLAY_COLORMODE OMAP_IOR(59, struct omapfb_ovl_colormode) 47 | #define OMAPFB_WAITFORGO OMAP_IO(60) 48 | #define OMAPFB_GET_VRAM_INFO OMAP_IOR(61, struct omapfb_vram_info) 49 | #define OMAPFB_SET_TEARSYNC OMAP_IOW(62, struct omapfb_tearsync_info) 50 | #define OMAPFB_GET_DISPLAY_INFO OMAP_IOR(63, struct omapfb_display_info) 51 | #define OMAPFB_CAPS_GENERIC_MASK 0x00000fff 52 | #define OMAPFB_CAPS_LCDC_MASK 0x00fff000 53 | #define OMAPFB_CAPS_PANEL_MASK 0xff000000 54 | #define OMAPFB_CAPS_MANUAL_UPDATE 0x00001000 55 | #define OMAPFB_CAPS_TEARSYNC 0x00002000 56 | #define OMAPFB_CAPS_PLANE_RELOCATE_MEM 0x00004000 57 | #define OMAPFB_CAPS_PLANE_SCALE 0x00008000 58 | #define OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE 0x00010000 59 | #define OMAPFB_CAPS_WINDOW_SCALE 0x00020000 60 | #define OMAPFB_CAPS_WINDOW_OVERLAY 0x00040000 61 | #define OMAPFB_CAPS_WINDOW_ROTATE 0x00080000 62 | #define OMAPFB_CAPS_SET_BACKLIGHT 0x01000000 63 | #define OMAPFB_FORMAT_MASK 0x00ff 64 | #define OMAPFB_FORMAT_FLAG_DOUBLE 0x0100 65 | #define OMAPFB_FORMAT_FLAG_TEARSYNC 0x0200 66 | #define OMAPFB_FORMAT_FLAG_FORCE_VSYNC 0x0400 67 | #define OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY 0x0800 68 | #define OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY 0x1000 69 | #define OMAPFB_MEMTYPE_SDRAM 0 70 | #define OMAPFB_MEMTYPE_SRAM 1 71 | #define OMAPFB_MEMTYPE_MAX 1 72 | #define OMAPFB_MEM_IDX_ENABLED 0x80 73 | #define OMAPFB_MEM_IDX_MASK 0x7f 74 | enum omapfb_color_format { 75 | OMAPFB_COLOR_RGB565 = 0, 76 | OMAPFB_COLOR_YUV422, 77 | OMAPFB_COLOR_YUV420, 78 | OMAPFB_COLOR_CLUT_8BPP, 79 | OMAPFB_COLOR_CLUT_4BPP, 80 | OMAPFB_COLOR_CLUT_2BPP, 81 | OMAPFB_COLOR_CLUT_1BPP, 82 | OMAPFB_COLOR_RGB444, 83 | OMAPFB_COLOR_YUY422, 84 | OMAPFB_COLOR_ARGB16, 85 | OMAPFB_COLOR_RGB24U, 86 | OMAPFB_COLOR_RGB24P, 87 | OMAPFB_COLOR_ARGB32, 88 | OMAPFB_COLOR_RGBA32, 89 | OMAPFB_COLOR_RGBX32, 90 | }; 91 | struct omapfb_update_window { 92 | __u32 x, y; 93 | __u32 width, height; 94 | __u32 format; 95 | __u32 out_x, out_y; 96 | __u32 out_width, out_height; 97 | __u32 reserved[8]; 98 | }; 99 | struct omapfb_update_window_old { 100 | __u32 x, y; 101 | __u32 width, height; 102 | __u32 format; 103 | }; 104 | enum omapfb_plane { 105 | OMAPFB_PLANE_GFX = 0, 106 | OMAPFB_PLANE_VID1, 107 | OMAPFB_PLANE_VID2, 108 | }; 109 | enum omapfb_channel_out { 110 | OMAPFB_CHANNEL_OUT_LCD = 0, 111 | OMAPFB_CHANNEL_OUT_DIGIT, 112 | }; 113 | struct omapfb_plane_info { 114 | __u32 pos_x; 115 | __u32 pos_y; 116 | __u8 enabled; 117 | __u8 channel_out; 118 | __u8 mirror; 119 | __u8 mem_idx; 120 | __u32 out_width; 121 | __u32 out_height; 122 | __u32 reserved2[12]; 123 | }; 124 | struct omapfb_mem_info { 125 | __u32 size; 126 | __u8 type; 127 | __u8 reserved[3]; 128 | }; 129 | struct omapfb_caps { 130 | __u32 ctrl; 131 | __u32 plane_color; 132 | __u32 wnd_color; 133 | }; 134 | enum omapfb_color_key_type { 135 | OMAPFB_COLOR_KEY_DISABLED = 0, 136 | OMAPFB_COLOR_KEY_GFX_DST, 137 | OMAPFB_COLOR_KEY_VID_SRC, 138 | }; 139 | struct omapfb_color_key { 140 | __u8 channel_out; 141 | __u32 background; 142 | __u32 trans_key; 143 | __u8 key_type; 144 | }; 145 | enum omapfb_update_mode { 146 | OMAPFB_UPDATE_DISABLED = 0, 147 | OMAPFB_AUTO_UPDATE, 148 | OMAPFB_MANUAL_UPDATE 149 | }; 150 | struct omapfb_memory_read { 151 | __u16 x; 152 | __u16 y; 153 | __u16 w; 154 | __u16 h; 155 | size_t buffer_size; 156 | void *buffer; 157 | }; 158 | struct omapfb_ovl_colormode { 159 | __u8 overlay_idx; 160 | __u8 mode_idx; 161 | __u32 bits_per_pixel; 162 | __u32 nonstd; 163 | struct fb_bitfield red; 164 | struct fb_bitfield green; 165 | struct fb_bitfield blue; 166 | struct fb_bitfield transp; 167 | }; 168 | struct omapfb_vram_info { 169 | __u32 total; 170 | __u32 free; 171 | __u32 largest_free_block; 172 | __u32 reserved[5]; 173 | }; 174 | struct omapfb_tearsync_info { 175 | __u8 enabled; 176 | __u8 reserved1[3]; 177 | __u16 line; 178 | __u16 reserved2; 179 | }; 180 | struct omapfb_display_info { 181 | __u16 xres; 182 | __u16 yres; 183 | __u32 width; 184 | __u32 height; 185 | __u32 reserved[5]; 186 | }; 187 | #endif 188 | -------------------------------------------------------------------------------- /common/wiz_video.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "warm/warm.h" 18 | 19 | static volatile unsigned short *memregs; 20 | static volatile unsigned int *memregl; 21 | int probably_caanoo; 22 | int memdev = -1; 23 | 24 | #define FB_BUF_COUNT 4 25 | static unsigned int fb_paddr[FB_BUF_COUNT]; 26 | static int fb_buf_count = FB_BUF_COUNT; 27 | static int fb_work_buf; 28 | static int fbdev = -1; 29 | 30 | static void *gp2x_screens[FB_BUF_COUNT]; 31 | static void *g_screen_ptr; 32 | 33 | 34 | static void vout_gp2x_flip(void) 35 | { 36 | memregl[0x406C>>2] = fb_paddr[fb_work_buf]; 37 | memregl[0x4058>>2] |= 0x10; 38 | 39 | fb_work_buf++; 40 | if (fb_work_buf >= fb_buf_count) 41 | fb_work_buf = 0; 42 | g_screen_ptr = gp2x_screens[fb_work_buf]; 43 | } 44 | 45 | static int vout_gp2x_init(int no_dblbuf) 46 | { 47 | struct fb_fix_screeninfo fbfix; 48 | int i, ret; 49 | 50 | memdev = open("/dev/mem", O_RDWR); 51 | if (memdev == -1) { 52 | perror("open(/dev/mem) failed"); 53 | exit(1); 54 | } 55 | 56 | memregs = mmap(0, 0x20000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000); 57 | if (memregs == MAP_FAILED) { 58 | perror("mmap(memregs) failed"); 59 | exit(1); 60 | } 61 | memregl = (volatile void *)memregs; 62 | 63 | fbdev = open("/dev/fb0", O_RDWR); 64 | if (fbdev < 0) { 65 | perror("can't open fbdev"); 66 | exit(1); 67 | } 68 | 69 | ret = ioctl(fbdev, FBIOGET_FSCREENINFO, &fbfix); 70 | if (ret == -1) { 71 | perror("ioctl(fbdev) failed"); 72 | exit(1); 73 | } 74 | 75 | printf("framebuffer: \"%s\" @ %08lx\n", fbfix.id, fbfix.smem_start); 76 | fb_paddr[0] = fbfix.smem_start; 77 | probably_caanoo = fb_paddr[0] >= 0x4000000; 78 | printf("looking like Caanoo? %s.\n", probably_caanoo ? "yes" : "no"); 79 | 80 | gp2x_screens[0] = mmap(0, 320*240*2*FB_BUF_COUNT, PROT_READ|PROT_WRITE, 81 | MAP_SHARED, memdev, fb_paddr[0]); 82 | if (gp2x_screens[0] == MAP_FAILED) 83 | { 84 | perror("mmap(gp2x_screens) failed"); 85 | exit(1); 86 | } 87 | memset(gp2x_screens[0], 0, 320*240*2*FB_BUF_COUNT); 88 | 89 | if (!no_dblbuf) { 90 | warm_init(); 91 | ret = warm_change_cb_range(WCB_B_BIT, 1, gp2x_screens[0], 320*240*2*FB_BUF_COUNT); 92 | if (ret != 0) 93 | fprintf(stderr, "could not make fb buferable.\n"); 94 | } 95 | 96 | // printf(" %p -> %08x\n", gp2x_screens[0], fb_paddr[0]); 97 | for (i = 1; i < FB_BUF_COUNT; i++) 98 | { 99 | fb_paddr[i] = fb_paddr[i-1] + 320*240*2; 100 | gp2x_screens[i] = (char *)gp2x_screens[i-1] + 320*240*2; 101 | // printf(" %p -> %08x\n", gp2x_screens[i], fb_paddr[i]); 102 | } 103 | fb_work_buf = 0; 104 | g_screen_ptr = gp2x_screens[0]; 105 | 106 | if (no_dblbuf) 107 | fb_buf_count = 1; 108 | 109 | return 0; 110 | } 111 | 112 | static void vout_gp2x_set_mode(int bpp, int rot) 113 | { 114 | int rot_cmd[2] = { 0, 0 }; 115 | int code = 0, bytes = 2; 116 | unsigned int r; 117 | int ret; 118 | 119 | if (probably_caanoo) 120 | rot = 0; 121 | 122 | rot_cmd[0] = rot ? 6 : 5; 123 | ret = ioctl(fbdev, _IOW('D', 90, int[2]), rot_cmd); 124 | if (ret < 0) 125 | perror("rot ioctl failed"); 126 | 127 | memregl[0x4004>>2] = rot ? 0x013f00ef : 0x00ef013f; 128 | memregl[0x4000>>2] |= 1 << 3; 129 | 130 | switch (bpp) 131 | { 132 | case 8: 133 | code = 0x443a; 134 | bytes = 1; 135 | break; 136 | 137 | case 15: 138 | case 16: 139 | code = 0x4432; 140 | bytes = 2; 141 | break; 142 | 143 | default: 144 | fprintf(stderr, "unhandled bpp request: %d\n", abs(bpp)); 145 | return; 146 | } 147 | 148 | memregl[0x405c>>2] = bytes; 149 | memregl[0x4060>>2] = bytes * (rot ? 240 : 320); 150 | 151 | r = memregl[0x4058>>2]; 152 | r = (r & 0xffff) | (code << 16) | 0x10; 153 | memregl[0x4058>>2] = r; 154 | } 155 | 156 | static void vout_gp2x_set_palette16(unsigned short *pal, int len) 157 | { 158 | int i; 159 | for (i = 0; i < len; i++) 160 | memregl[0x4070>>2] = (i << 24) | pal[i]; 161 | } 162 | 163 | static void vout_gp2x_set_palette32(unsigned int *pal, int len) 164 | { 165 | /* pollux palette is 16bpp only.. */ 166 | int i; 167 | for (i = 0; i < len; i++) 168 | { 169 | int c = pal[i]; 170 | c = ((c >> 8) & 0xf800) | ((c >> 5) & 0x07c0) | ((c >> 3) & 0x001f); 171 | memregl[0x4070>>2] = (i << 24) | c; 172 | } 173 | } 174 | 175 | void vout_gp2x_finish(void) 176 | { 177 | if (memregl != NULL) { 178 | if (memregl[0x4058>>2] & 0x10) 179 | usleep(100000); 180 | if (memregl[0x4058>>2] & 0x10) 181 | printf("MLCCONTROL1 dirty? %08x %08x\n", 182 | memregl[0x406C>>2], memregl[0x4058>>2]); 183 | 184 | memregl[0x406C>>2] = fb_paddr[0]; 185 | memregl[0x4058>>2] |= 0x10; 186 | munmap((void *)memregs, 0x20000); 187 | memregs = NULL; 188 | memregl = NULL; 189 | } 190 | 191 | close(fbdev); 192 | close(memdev); 193 | 194 | warm_finish(); 195 | } 196 | 197 | -------------------------------------------------------------------------------- /common/wiz_video_arm.s: -------------------------------------------------------------------------------- 1 | @ vim:filetype=armasm 2 | 3 | @ input: r2-r5 4 | @ output: r7,r8 5 | @ trash: r6 6 | .macro rb_line_low 7 | mov r6, r2, lsl #16 8 | mov r7, r3, lsl #16 9 | orr r7, r7, r6, lsr #16 10 | mov r6, r4, lsl #16 11 | mov r8, r5, lsl #16 12 | orr r8, r8, r6, lsr #16 13 | .endm 14 | 15 | .macro rb_line_hi 16 | mov r6, r2, lsr #16 17 | mov r7, r3, lsr #16 18 | orr r7, r6, r7, lsl #16 19 | mov r6, r4, lsr #16 20 | mov r8, r5, lsr #16 21 | orr r8, r6, r8, lsl #16 22 | .endm 23 | 24 | .global rotated_blit16 @ void *dst, void *linesx4 25 | rotated_blit16: 26 | stmfd sp!,{r4-r8,lr} 27 | 28 | sub r0, r0, #240*2 @ adjust 29 | mov lr, #240/4 30 | 31 | rotated_blit_loop16_o: 32 | orr lr, lr, #((320/4)-1) << 16 33 | add r0, r0, #(240*320)*2 34 | 35 | rotated_blit_loop16: 36 | ldr r2, [r1, #320*0*2] 37 | ldr r3, [r1, #320*1*2] 38 | ldr r4, [r1, #320*2*2] 39 | ldr r5, [r1, #320*3*2] 40 | rb_line_low 41 | stmia r0, {r7,r8} 42 | sub r0, r0, #240*2 43 | rb_line_hi 44 | stmia r0, {r7,r8} 45 | sub r0, r0, #240*2 46 | 47 | ldr r2, [r1, #320*0*2+4] 48 | ldr r3, [r1, #320*1*2+4] 49 | ldr r4, [r1, #320*2*2+4] 50 | ldr r5, [r1, #320*3*2+4] 51 | rb_line_low 52 | stmia r0, {r7,r8} 53 | sub r0, r0, #240*2 54 | rb_line_hi 55 | stmia r0, {r7,r8} 56 | sub r0, r0, #240*2 57 | 58 | subs lr, lr, #1<<16 59 | add r1, r1, #8 60 | bpl rotated_blit_loop16 61 | 62 | add lr, lr, #1<<16 63 | subs lr, lr, #1 64 | 65 | add r0, r0, #4*2 66 | add r1, r1, #(320*3)*2 67 | bgt rotated_blit_loop16_o 68 | 69 | ldmfd sp!,{r4-r8,pc} 70 | 71 | 72 | .global rotated_blit8 @ void *dst, void *linesx4 73 | rotated_blit8: 74 | stmfd sp!,{r4-r8,lr} 75 | 76 | mov r8, #320 77 | sub r0, r0, #240 @ adjust 78 | mov lr, #240/4 79 | 80 | rotated_blit8_loop_o: 81 | orr lr, lr, #((320/4)-1) << 16 82 | add r0, r0, #(240*320) 83 | 84 | rotated_blit8_loop: 85 | mov r6, r1 86 | ldr r2, [r6], r8 87 | ldr r3, [r6], r8 88 | ldr r4, [r6], r8 89 | ldr r5, [r6], r8 90 | 91 | mov r6, r2, lsl #24 92 | mov r6, r6, lsr #8 93 | orr r6, r6, r3, lsl #24 94 | mov r6, r6, lsr #8 95 | orr r6, r6, r4, lsl #24 96 | mov r6, r6, lsr #8 97 | orr r6, r6, r5, lsl #24 98 | str r6, [r0], #-240 99 | 100 | and r6, r3, #0xff00 101 | and r7, r2, #0xff00 102 | orr r6, r6, r7, lsr #8 103 | and r7, r4, #0xff00 104 | orr r6, r6, r7, lsl #8 105 | and r7, r5, #0xff00 106 | orr r6, r6, r7, lsl #16 107 | str r6, [r0], #-240 108 | 109 | and r6, r4, #0xff0000 110 | and r7, r2, #0xff0000 111 | orr r6, r6, r7, lsr #16 112 | and r7, r3, #0xff0000 113 | orr r6, r6, r7, lsr #8 114 | and r7, r5, #0xff0000 115 | orr r6, r6, r7, lsl #8 116 | str r6, [r0], #-240 117 | 118 | mov r6, r5, lsr #24 119 | mov r6, r6, lsl #8 120 | orr r6, r6, r4, lsr #24 121 | mov r6, r6, lsl #8 122 | orr r6, r6, r3, lsr #24 123 | mov r6, r6, lsl #8 124 | orr r6, r6, r2, lsr #24 125 | str r6, [r0], #-240 126 | 127 | subs lr, lr, #1<<16 128 | add r1, r1, #4 129 | bpl rotated_blit8_loop 130 | 131 | add lr, lr, #1<<16 132 | subs lr, lr, #1 133 | 134 | add r0, r0, #4 135 | add r1, r1, #320*3 136 | bgt rotated_blit8_loop_o 137 | 138 | ldmfd sp!,{r4-r8,pc} 139 | 140 | -------------------------------------------------------------------------------- /dist/ginge.gpe: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | unset LD_PRELOAD 4 | 5 | /sbin/rmmod warm 2> /dev/null 6 | /sbin/insmod ./tools/warm_2.6.24.ko 7 | 8 | # we might write something to /tmp, no need to write to flash 9 | # FIXME: doesn't seem to work, tmpfs broken in Wiz kernel? 10 | #if ! grep -q '/tmp' /proc/mounts; then 11 | # mount -t tmpfs none /tmp 12 | #fi 13 | 14 | # theoretically GP2X apps can make use of more RAM, because 15 | # Wiz has 2.6 kernel (larger memory requirements) and larger 16 | # reserved areas, so we mount some swap here just in case. 17 | 18 | # FIXME: doesn't work too, causes more problems than resolves 19 | #mkswap swapfile 20 | #swapon swapfile 21 | 22 | ./gp2xmenu --view-game 23 | 24 | #swapoff swapfile 25 | #umount /tmp 26 | /sbin/rmmod warm 2> /dev/null 27 | 28 | cd /usr/gp2x/ 29 | exec ./gp2xmenu 30 | -------------------------------------------------------------------------------- /dist/ginge.ini: -------------------------------------------------------------------------------- 1 | [info] 2 | name="Ginge Is Not Gp2x Emulator" 3 | path="/ginge/ginge.gpe" 4 | icon="/ginge/ginge.png" 5 | title="/ginge/ginge_banner.png" 6 | -------------------------------------------------------------------------------- /dist/ginge.pxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GINGE 6 | 7 | 8 | 9 | 10 | 11 | 12 | GINGE 13 | 14 | GINGE 15 | 16 | 17 | GINGE - GINGE Is Not GP2X Emulator 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /dist/ginge.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export FBDEV=/dev/fb1 4 | ofbset -fb $FBDEV -pos 80 0 -size 640 480 -mem 614400 -en 1 5 | fbset -fb $FBDEV -g 320 240 320 480 16 6 | 7 | # make it runnable from ssh 8 | if [ -z "$DISPLAY" ]; then 9 | export DISPLAY=:0 10 | fi 11 | 12 | ./gp2xmenu 13 | 14 | ofbset -fb $FBDEV -pos 0 0 -size 0 0 -mem 0 -en 0 15 | -------------------------------------------------------------------------------- /dist/ginge26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notaz/ginge/8cb68a18cc36ab8e47b8834723f3a65ea360974f/dist/ginge26.png -------------------------------------------------------------------------------- /dist/ginge32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notaz/ginge/8cb68a18cc36ab8e47b8834723f3a65ea360974f/dist/ginge32.png -------------------------------------------------------------------------------- /dist/ginge60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notaz/ginge/8cb68a18cc36ab8e47b8834723f3a65ea360974f/dist/ginge60.png -------------------------------------------------------------------------------- /dist/ginge_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notaz/ginge/8cb68a18cc36ab8e47b8834723f3a65ea360974f/dist/ginge_banner.png -------------------------------------------------------------------------------- /dist/ginge_dyn_eabi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | root=$1 4 | shift 5 | 6 | loader="${root}lib/ld-linux.so.2" 7 | export LD_LIBRARY_PATH="${root}lib" 8 | export LD_PRELOAD="${root}ginge_dyn" 9 | 10 | export GINGE_ROOT="${root}" 11 | 12 | exec "${loader}" "$@" 13 | -------------------------------------------------------------------------------- /dist/ginge_dyn_oabi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | root=$1 4 | shift 5 | 6 | export LD_LIBRARY_PATH="${root}lib" 7 | export LD_PRELOAD="${root}ginge_dyn" 8 | 9 | export GINGE_ROOT="${root}" 10 | 11 | exec "$@" 12 | -------------------------------------------------------------------------------- /dist/make_cmn.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | rm -rf ${out} 6 | mkdir -p ${out} 7 | cp -r gp2xmenu/gp2xmenu_data ${out}/ 8 | cp prep/ginge_prep${tag} ${out}/ginge_prep 9 | cp loader/ginge_dyn${tag} ${out}/ginge_dyn 10 | cp loader/ginge_sloader${tag} ${out}/ginge_sloader 11 | cp readme.txt ${out}/ 12 | 13 | -------------------------------------------------------------------------------- /loader/Makefile: -------------------------------------------------------------------------------- 1 | ARCH ?= arm 2 | CROSS_COMPILE ?= arm-linux- 3 | CC = $(CROSS_COMPILE)gcc 4 | AS = $(CROSS_COMPILE)as 5 | CFLAGS += -Wall -ggdb -DLOADER 6 | LDFLAGS += -ggdb 7 | #LDFLAGS += -nodefaultlibs # NYET 8 | ifndef DEBUG 9 | CFLAGS += -O2 -fno-strict-aliasing 10 | LDFLAGS += -O2 11 | endif 12 | ifdef DBG 13 | CFLAGS += -DDBG=$(DBG) 14 | endif 15 | 16 | ifeq "$(ARCH)" "ia32" 17 | ARCH = ia32 18 | CFLAGS += -m32 19 | LDFLAGS += -m32 20 | endif 21 | ifeq "$(ARCH)" "arm" 22 | OBJ += syscalls.o emu_arm.o 23 | endif 24 | ifdef PND 25 | CFLAGS += -DPND 26 | TAG = _pnd 27 | endif 28 | ifdef WIZ 29 | CFLAGS += -DWIZ 30 | OBJ += wiz_video_arm.o 31 | TAG = _wiz 32 | endif 33 | 34 | vpath %.c = ../common/ 35 | vpath %.s = ../common/ 36 | 37 | TARGET_S = ginge_sloader$(TAG) 38 | TARGET_D = ginge_dyn$(TAG) 39 | 40 | OBJ += emu.o host.o host_fb.o cmn.o llibc.o 41 | OBJ_S += $(OBJ) loader.o loader_$(ARCH).o patches.o 42 | OBJ_D += $(OBJ) dl.o 43 | 44 | all: $(TARGET_S) $(TARGET_D) 45 | 46 | $(TARGET_S): LDFLAGS += -Wl,-T script_$(ARCH).lds 47 | $(TARGET_D): LDFLAGS += -ldl -Wl,--version-script=ginge_dyn.symver 48 | $(TARGET_D): LDFLAGS += -Wl,--no-undefined 49 | 50 | $(TARGET_S): $(OBJ_S) 51 | $(CC) -o $@ $^ -static $(LDFLAGS) 52 | 53 | $(TARGET_D): $(OBJ_D) 54 | $(CC) -o $@ $^ -shared $(LDFLAGS) 55 | 56 | # easier to support old toolchains with this 57 | %.o: %.s 58 | $(CC) -o $@ -c $< $(CFLAGS) 59 | 60 | clean: 61 | $(RM) $(TARGET_S) $(TARGET_D) $(OBJ_S) $(OBJ_D) 62 | 63 | # basic deps 64 | dl.o patches.o: override.c 65 | *.o: header.h 66 | host_fb.o: libpicofe/linux/fbdev.c 67 | -------------------------------------------------------------------------------- /loader/dl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | #define _GNU_SOURCE 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "header.h" 13 | 14 | #define DL 15 | #include "override.c" 16 | 17 | static void next_line(FILE *f) 18 | { 19 | int c; 20 | do { 21 | c = fgetc(f); 22 | } while (c != EOF && c != '\n'); 23 | } 24 | 25 | __attribute__((constructor)) 26 | static void ginge_init(void) 27 | { 28 | void *lowest_segments[2] = { NULL, NULL }; 29 | unsigned int start, end; 30 | int i, ret; 31 | FILE *f; 32 | 33 | for (i = 0; i < ARRAY_SIZE(real_funcs_np); i++) { 34 | *real_funcs_np[i].func_ptr = dlsym(RTLD_NEXT, real_funcs_np[i].name); 35 | if (*real_funcs_np[i].func_ptr == NULL) { 36 | fprintf(stderr, "dlsym %s: %s\n", real_funcs_np[i].name, dlerror()); 37 | exit(1); 38 | } 39 | // dbg("%p %s\n", *real_funcs_np[i].func_ptr, real_funcs_np[i].name); 40 | } 41 | 42 | f = fopen("/proc/self/maps", "r"); 43 | if (f == NULL) { 44 | perror("open /proc/self/maps"); 45 | exit(1); 46 | } 47 | 48 | ret = fscanf(f, "%x-%x ", &start, &end); 49 | if (ret != 2) { 50 | perror("parse maps"); 51 | exit(1); 52 | } 53 | lowest_segments[0] = (void *)start; 54 | 55 | while (1) { 56 | next_line(f); 57 | 58 | ret = fscanf(f, "%x-%*s %*s %*s %*s %*s %*s\n", &start); 59 | if (ret <= 0) 60 | break; 61 | 62 | if (lowest_segments[0] == NULL || (void *)start < lowest_segments[0]) 63 | lowest_segments[0] = (void *)start; 64 | else if (lowest_segments[1] == NULL 65 | && (char *)start - (char *)lowest_segments[0] > 0x800000) 66 | { 67 | // an offset is needed because ld-linux also 68 | // tends to put stuff here 69 | lowest_segments[1] = (void *)(start - 0x20000); 70 | } 71 | } 72 | 73 | #if 0 74 | rewind(f); 75 | char buff[256]; 76 | while (fgets(buff, sizeof(buff), f)) 77 | fputs(buff, stdout); 78 | #endif 79 | fclose(f); 80 | 81 | // remove self from preload, further commands (from system() and such) 82 | // will be handled by ginge_prep. 83 | unsetenv("LD_PRELOAD"); 84 | unsetenv("LD_LIBRARY_PATH"); 85 | 86 | emu_init(lowest_segments, 1); 87 | } 88 | 89 | // vim:shiftwidth=2:expandtab 90 | -------------------------------------------------------------------------------- /loader/emu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011,2016 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | // a "gentle" reminder 8 | #ifdef __ARM_EABI__ 9 | #error loader is meant to be OABI! 10 | #endif 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "header.h" 35 | #include "../common/host_fb.h" 36 | #include "../common/cmn.h" 37 | #include "syscalls.h" 38 | #include "realfuncs.h" 39 | #include "llibc.h" 40 | 41 | #if (DBG & 2) && !(DBG & 4) 42 | #define LOG_IO_UNK 43 | #endif 44 | #if (DBG & 4) 45 | #define LOG_IO 46 | #endif 47 | //#define LOG_SEGV 48 | 49 | #ifdef LOG_IO 50 | #define iolog log_io 51 | #else 52 | #define iolog(...) 53 | #endif 54 | 55 | #ifdef LOG_IO_UNK 56 | #define iolog_unh log_io 57 | #else 58 | #define iolog_unh(...) 59 | #endif 60 | 61 | #ifdef LOG_SEGV 62 | #define segvlog g_printf 63 | #else 64 | #define segvlog(...) 65 | #endif 66 | 67 | #if defined(LOG_IO) || defined(LOG_IO_UNK) 68 | #include "mmsp2-regs.h" 69 | #endif 70 | 71 | typedef unsigned long long u64; 72 | typedef unsigned int u32; 73 | typedef unsigned short u16; 74 | typedef unsigned char u8; 75 | 76 | #define THREAD_STACK_SIZE 0x200000 77 | 78 | static int fb_sync_thread_paused; 79 | static int fb_sync_thread_futex; 80 | 81 | static int emu_is_dl; 82 | static int emu_force_snd_frag; 83 | 84 | static struct { 85 | u32 dstctrl; 86 | u32 dstaddr; 87 | u32 dststride; 88 | u32 srcctrl; 89 | u32 srcaddr; // 90 | u32 srcstride; 91 | u32 srcforcolor; 92 | u32 srcbackcolor; 93 | u32 patctrl; // 94 | u32 patforcolor; 95 | u32 patbackcolor; 96 | u32 size; 97 | u32 ctrl; // 98 | u32 run; 99 | u32 intc; 100 | u32 srcfifo; 101 | } blitter; 102 | 103 | #define SRCCTRL_INVIDEO (1 << 8) 104 | #define SRCCTRL_SRCENB (1 << 7) 105 | #define CTRL_TRANSPARENCYENB (1 << 11) 106 | 107 | static struct { 108 | // mmsp2 109 | u16 mlc_stl_cntl; 110 | union { 111 | u32 mlc_stl_adr; // mlcaddress for pollux 112 | struct { 113 | u16 mlc_stl_adrl; 114 | u16 mlc_stl_adrh; 115 | }; 116 | }; 117 | u16 mlc_stl_pallt_a; 118 | union { 119 | u16 mlc_stl_pallt_d[256*2]; 120 | u32 mlc_stl_pallt_d32[256]; 121 | }; 122 | 123 | // pollux 124 | u32 mlccontrol; 125 | u16 mlcpalette[256]; 126 | 127 | // state 128 | void *umem; 129 | u32 old_mlc_stl_adr; 130 | u32 btn_state; // as seen through /dev/GPIO: 0PVdVu YXBA RLSeSt 0Ri0Dn 0Le0Up 131 | struct { 132 | u32 width, height; 133 | u32 stride; 134 | u32 bpp; 135 | u32 dirty_pal:2; 136 | } v; 137 | } mmsp2; 138 | #define pollux mmsp2 // so that code doesn't look that weird 139 | enum { 140 | DIRTY_PAL_MMSP2 = 1, 141 | DIRTY_PAL_POLLUX = 2, 142 | }; 143 | 144 | 145 | #if defined(LOG_IO) || defined(LOG_IO_UNK) 146 | static void log_io(const char *pfx, u32 a, u32 d, int size) 147 | { 148 | const char *fmt, *reg = ""; 149 | switch (size) { 150 | case 8: fmt = "%s %08x %02x %s\n"; d &= 0xff; break; 151 | case 32: fmt = "%s %08x %08x %s\n"; break; 152 | default: fmt = "%s %08x %04x %s\n"; d &= 0xffff; break; 153 | } 154 | 155 | if ((a & ~0xffff) == 0x7f000000) 156 | reg = regnames[a & 0xffff]; 157 | 158 | g_printf(fmt, pfx, a, d, reg); 159 | } 160 | #endif 161 | 162 | static void memset16(void *dst, u32 pattern, int count) 163 | { 164 | u32 *dl; 165 | u16 *d; 166 | 167 | d = (u16 *)((long)dst & ~1); 168 | if ((long)d & 2) { 169 | *d++ = pattern; 170 | count--; 171 | } 172 | dl = (void *)d; 173 | pattern |= pattern << 16; 174 | 175 | while (count >= 2) { 176 | *dl++ = pattern; 177 | count -= 2; 178 | } 179 | if (count) 180 | *(u16 *)dl = pattern; 181 | } 182 | 183 | static void blt_tr(void *dst, void *src, u32 trc, int w) 184 | { 185 | u16 *d = (u16 *)((long)dst & ~1); 186 | u16 *s = (u16 *)((long)src & ~1); 187 | 188 | // XXX: optimize 189 | for (; w > 0; d++, s++, w--) 190 | if (*s != trc) 191 | *d = *s; 192 | } 193 | 194 | #define dump_blitter() \ 195 | { \ 196 | u32 *r = &blitter.dstctrl; \ 197 | int i; \ 198 | for (i = 0; i < 4*4; i++, r++) { \ 199 | g_printf("%08x ", *r); \ 200 | if ((i & 3) == 3) \ 201 | g_printf("\n"); \ 202 | } \ 203 | } 204 | 205 | static void *uppermem_lookup(u32 addr, u8 **mem_end) 206 | { 207 | // XXX: maybe support mirroring? 208 | if ((addr & 0xfe000000) != 0x02000000) 209 | return NULL; 210 | 211 | *mem_end = (u8 *)mmsp2.umem + 0x02000000; 212 | return (u8 *)mmsp2.umem - 0x02000000 + addr; 213 | } 214 | 215 | static void blitter_do(void) 216 | { 217 | u8 *dst, *dste, *src = NULL, *srce = NULL; 218 | int w, h, sstrd, dstrd; 219 | int to_screen = 0; 220 | u32 bpp, addr; 221 | 222 | w = blitter.size & 0x7ff; 223 | h = (blitter.size >> 16) & 0x7ff; 224 | sstrd = blitter.srcstride; 225 | dstrd = blitter.dststride; 226 | 227 | // XXX: need to confirm this.. 228 | addr = (blitter.dstaddr & ~3) | ((blitter.dstctrl & 0x1f) >> 3); 229 | 230 | // use dst bpp.. How does it do blits with different src bpp? 231 | bpp = (blitter.dstctrl & 0x20) ? 16 : 8; 232 | 233 | // maybe the screen? 234 | if (((w == 320 && h == 240) || // blit whole screen 235 | (w * h >= 320*240/2)) && // ..or at least half of the area 236 | mmsp2.mlc_stl_adr <= addr && addr < mmsp2.mlc_stl_adr + 320*240*2) 237 | to_screen = 1; 238 | 239 | dst = uppermem_lookup(addr, &dste); 240 | 241 | // XXX: assume fill if no SRCENB, but it could be pattern blit.. 242 | if (blitter.srcctrl & SRCCTRL_SRCENB) { 243 | if (!(blitter.srcctrl & SRCCTRL_INVIDEO)) 244 | goto bad_blit; 245 | 246 | addr = (blitter.srcaddr & ~3) | ((blitter.srcctrl & 0x1f) >> 3); 247 | src = uppermem_lookup(addr, &srce); 248 | if (src == NULL) 249 | goto bad_blit; 250 | 251 | if (src + sstrd * h > srce) { 252 | err("blit %08x->%08x %dx%d did not fit src\n", 253 | blitter.srcaddr, blitter.dstaddr, w, h); 254 | h = (srce - src) / sstrd; 255 | } 256 | } 257 | 258 | if (dst == NULL) 259 | goto bad_blit; 260 | 261 | if (dst + dstrd * h > dste) { 262 | err("blit %08x->%08x %dx%d did not fit dst\n", 263 | blitter.srcaddr, blitter.dstaddr, w, h); 264 | h = (dste - dst) / dstrd; 265 | } 266 | 267 | if (src != NULL) { 268 | // copy 269 | if (bpp == 16 && (blitter.ctrl & CTRL_TRANSPARENCYENB)) { 270 | u32 trc = blitter.ctrl >> 16; 271 | for (; h > 0; h--, dst += dstrd, src += sstrd) 272 | blt_tr(dst, src, trc, w); 273 | } 274 | else { 275 | for (; h > 0; h--, dst += dstrd, src += sstrd) 276 | memcpy(dst, src, w * bpp / 8); 277 | } 278 | } 279 | else { 280 | // fill. Assume the pattern is cleared and bg color is used 281 | u32 bgc = blitter.patbackcolor & 0xffff; 282 | if (bpp == 16) { 283 | for (; h > 0; h--, dst += dstrd) 284 | memset16(dst, bgc, w); 285 | } 286 | else { 287 | for (; h > 0; h--, dst += dstrd) 288 | memset(dst, bgc, w); // bgc? 289 | } 290 | } 291 | 292 | if (to_screen) { 293 | fb_sync_thread_futex = 1; 294 | g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL); 295 | } 296 | return; 297 | 298 | bad_blit: 299 | err("blit %08x->%08x %dx%d translated to %p->%p\n", 300 | blitter.srcaddr, blitter.dstaddr, w, h, src, dst); 301 | dump_blitter(); 302 | } 303 | 304 | // FIXME: pass real dimensions to blitters 305 | static void mlc_flip(void *src, int bpp, int stride) 306 | { 307 | static int old_bpp; 308 | 309 | // only pass pal to host if it's dirty 310 | if (bpp <= 8 && mmsp2.v.dirty_pal) { 311 | if (mmsp2.v.dirty_pal == DIRTY_PAL_MMSP2) 312 | host_video_update_pal32(mmsp2.mlc_stl_pallt_d32); 313 | else 314 | host_video_update_pal16(mmsp2.mlcpalette); 315 | mmsp2.v.dirty_pal = 0; 316 | } 317 | 318 | if (bpp != old_bpp) { 319 | host_video_change_bpp(bpp); 320 | old_bpp = bpp; 321 | } 322 | 323 | switch (bpp) { 324 | case 4: 325 | host_video_blit4(src, 320, 240, stride); 326 | break; 327 | 328 | case 8: 329 | host_video_blit8(src, 320, 240, stride); 330 | break; 331 | 332 | case 16: 333 | host_video_blit16(src, 320, 240, stride); 334 | break; 335 | 336 | case 24: 337 | // TODO 338 | break; 339 | } 340 | } 341 | 342 | static void *fb_sync_thread(void *arg) 343 | { 344 | unsigned long sigmask[2] = { ~0ul, ~0ul }; 345 | struct timespec ts = { 0, 0 }; 346 | int invalid_fb_addr = 1; 347 | int manual_refresh = 0; 348 | int frame_counter = 0; 349 | int wait_ret; 350 | 351 | // this thread can't run any signal handlers since the 352 | // app's stack/tls stuff will never be set up here 353 | sigmask[0] &= ~(1ul << (SIGSEGV - 1)); 354 | g_rt_sigprocmask_raw(SIG_SETMASK, sigmask, NULL, sizeof(sigmask)); 355 | 356 | //ret = setpriority(PRIO_PROCESS, 0, -1); 357 | //log("setpriority %d\n", ret); 358 | 359 | // tell the main thread we're done init 360 | fb_sync_thread_futex = 0; 361 | g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL); 362 | 363 | while (1) { 364 | u8 *gp2x_fb, *gp2x_fb_end; 365 | 366 | wait_ret = g_futex_raw(&fb_sync_thread_futex, FUTEX_WAIT, 0, &ts); 367 | 368 | // this is supposed to be done atomically, but to make life 369 | // easier ignore it for now, race impact is low anyway 370 | fb_sync_thread_futex = 0; 371 | 372 | if (wait_ret != 0 && wait_ret != -EWOULDBLOCK 373 | && wait_ret != -ETIMEDOUT) 374 | { 375 | err("fb_thread: futex error: %d\n", wait_ret); 376 | sleep(1); 377 | goto check_keys; 378 | } 379 | if (fb_sync_thread_paused) { 380 | ts.tv_nsec = 100000000; 381 | goto check_keys; 382 | } 383 | 384 | if (wait_ret == 0) { 385 | ts.tv_nsec = 50000000; 386 | manual_refresh++; 387 | if (manual_refresh == 2) 388 | dbg("fb_thread: switch to manual refresh\n"); 389 | } else { 390 | ts.tv_nsec = 16666667; 391 | if (manual_refresh > 1) 392 | dbg("fb_thread: switch to auto refresh\n"); 393 | manual_refresh = 0; 394 | } 395 | 396 | gp2x_fb = uppermem_lookup(mmsp2.mlc_stl_adr, &gp2x_fb_end); 397 | if (gp2x_fb == NULL || gp2x_fb + 320*240 * mmsp2.v.bpp / 8 > gp2x_fb_end) { 398 | if (!invalid_fb_addr) { 399 | err("fb_thread: %08x is out of range\n", mmsp2.mlc_stl_adr); 400 | invalid_fb_addr = 1; 401 | } 402 | continue; 403 | } 404 | 405 | invalid_fb_addr = 0; 406 | mlc_flip(gp2x_fb, mmsp2.v.bpp, mmsp2.v.stride); 407 | 408 | frame_counter++; 409 | if (frame_counter & 0x0f) 410 | continue; 411 | 412 | check_keys: 413 | // this is to check for kill key, in case main thread hung 414 | // or something else went wrong. 415 | pollux.btn_state = host_read_btns(); 416 | } 417 | } 418 | 419 | static void fb_thread_pause(void) 420 | { 421 | fb_sync_thread_paused = 1; 422 | // wait until it finishes last refresh 423 | // that it might be doing now 424 | usleep(10000); 425 | } 426 | 427 | static void fb_thread_resume(void) 428 | { 429 | fb_sync_thread_paused = 0; 430 | } 431 | 432 | static u32 xread32_io_cmn(u32 a, u32 *handled) 433 | { 434 | struct timespec ts; 435 | u32 d = 0; 436 | 437 | *handled = 1; 438 | switch (a) { 439 | // Wiz stuff 440 | case 0x1980: // TIMER3 TMRCOUNT 441 | // assume the timer is set up for microsec time 442 | g_clock_gettime_raw(CLOCK_REALTIME, &ts); 443 | d = ts.tv_sec * 1000000 + ((u64)(u32)ts.tv_nsec * 4294968 >> 32); 444 | break; 445 | case 0x402c: // MLCVSTRIDE0 446 | case 0x4060: // MLCVSTRIDE1 447 | d = pollux.v.stride; 448 | break; 449 | case 0x4038: // MLCADDRESS0 450 | case 0x406c: // MLCADDRESS1 451 | d = pollux.mlc_stl_adr; 452 | break; 453 | // wiz_lib reads: 454 | // ???? ???? YXBA DURiLe ???? VdVuMS LR?? ???? 455 | // | GPIOC[31:16] | GPIOB[31:16] | 456 | case 0xa058: // GPIOBPAD 457 | d = (pollux.btn_state >> 1) & 0x0100; 458 | d |= (pollux.btn_state << 1) & 0x0200; 459 | d |= (pollux.btn_state >> 3) & 0x0080; 460 | d |= (pollux.btn_state >> 5) & 0x0040; 461 | d |= (pollux.btn_state >> 6) & 0x0c00; 462 | d <<= 16; 463 | d = ~d; 464 | break; 465 | case 0xa098: // GPIOCPAD 466 | pollux.btn_state = host_read_btns(); 467 | d = (pollux.btn_state >> 8) & 0x00f0; 468 | d |= (pollux.btn_state >> 1) & 0x0008; 469 | d |= (pollux.btn_state << 2) & 0x0004; 470 | d |= (pollux.btn_state >> 5) & 0x0002; 471 | d |= (pollux.btn_state >> 2) & 0x0001; 472 | d <<= 16; 473 | d = ~d; 474 | break; 475 | default: 476 | *handled = 0; 477 | break; 478 | } 479 | 480 | return d; 481 | } 482 | 483 | static u32 xread8(u32 a) 484 | { 485 | iolog("r8 ", a, 0, 8); 486 | iolog_unh("r8 ", a, 0, 8); 487 | return 0; 488 | } 489 | 490 | static u32 xread16(u32 a) 491 | { 492 | static u32 fudge, old_a; 493 | u32 d = 0, t; 494 | 495 | if ((a & 0xffff0000) == 0x7f000000) { 496 | u32 a_ = a & 0xffff; 497 | switch (a_) { 498 | case 0x0910: // FPLL 499 | case 0x0912: 500 | d = 0x9407; 501 | break; 502 | // minilib reads as: 503 | // 0000 P000 VuVd00 0000 YXBA RLSeSt 0Ri0D 0Le0U 504 | // | GPIOD |GPIOC[8:15]|GPIOM[0:7] | 505 | // /dev/GPIO: 506 | // ... 0PVdVu ... 507 | case 0x1184: // GPIOC 508 | d = ~mmsp2.btn_state & 0xff00; 509 | d |= 0x00ff; 510 | break; 511 | case 0x1186: // GPIOD 512 | t = ~mmsp2.btn_state; 513 | d = (t >> 9) & 0x0080; 514 | d |= (t >> 11) & 0x0040; 515 | d |= (t >> 7) & 0x0800; 516 | d |= 0x373b; 517 | break; 518 | case 0x1198: // GPIOM 519 | mmsp2.btn_state = host_read_btns(); 520 | d = ~mmsp2.btn_state & 0xff; 521 | d |= 0x01aa; 522 | break; 523 | case 0x1836: // reserved 524 | d = 0x2330; 525 | break; 526 | case 0x2816: // DPC_X_MAX 527 | d = 319; 528 | break; 529 | case 0x2818: // DPC_Y_MAX 530 | d = 239; 531 | break; 532 | case 0x28da: 533 | d = mmsp2.mlc_stl_cntl; 534 | break; 535 | case 0x290e: 536 | case 0x2912: 537 | d = mmsp2.mlc_stl_adrl; 538 | break; 539 | case 0x2910: 540 | case 0x2914: 541 | d = mmsp2.mlc_stl_adrh; 542 | break; 543 | case 0x2958: 544 | d = mmsp2.mlc_stl_pallt_a; 545 | break; 546 | 547 | default: 548 | d = xread32_io_cmn(a_, &t); 549 | if (!t) 550 | goto unk; 551 | if (!(a_ & 2)) 552 | d >>= 16; 553 | break; 554 | } 555 | goto out; 556 | } 557 | 558 | unk: 559 | if (a == old_a) { 560 | d = fudge; 561 | fudge = ~fudge; 562 | } 563 | old_a = a; 564 | iolog_unh("r16", a, d & 0xffff, 16); 565 | 566 | out: 567 | d &= 0xffff; 568 | iolog("r16", a, d, 16); 569 | return d; 570 | } 571 | 572 | static u32 xread32(u32 a) 573 | { 574 | u32 d = 0; 575 | if ((a & 0xfff00000) == 0x7f000000) { 576 | u32 a_ = a & 0xffff; 577 | struct timespec ts; 578 | u64 t64; 579 | u32 t; 580 | 581 | switch (a_) { 582 | case 0x0a00: // TCOUNT, 1/7372800s 583 | g_clock_gettime_raw(CLOCK_REALTIME, &ts); 584 | t64 = (u64)ts.tv_sec * 1000000000 + ts.tv_nsec; 585 | // t * 7372800.0 / 1000000000 * 0x100000000 ~= t * 31665935 586 | t64 *= 31665935; 587 | d = t64 >> 32; 588 | break; 589 | 590 | default: 591 | d = xread32_io_cmn(a_, &t); 592 | if (!t) 593 | goto unh; 594 | break; 595 | } 596 | goto out; 597 | } 598 | if ((a & 0xfff00000) == 0x7f100000) { 599 | u32 *bl = &blitter.dstctrl; 600 | u32 a_ = a & 0xfff; 601 | if (a_ < 0x40) { 602 | d = bl[a_ / 4]; 603 | if (a_ == 0x34) 604 | d = 0; // not busy 605 | goto out; 606 | } 607 | } 608 | 609 | unh: 610 | iolog_unh("r32", a, d, 32); 611 | 612 | out: 613 | iolog("r32", a, d, 32); 614 | return d; 615 | } 616 | 617 | static void xwrite8(u32 a, u32 d) 618 | { 619 | iolog("w8 ", a, d, 8); 620 | iolog_unh("w8 ", a, d, 8); 621 | } 622 | 623 | static void xwrite16(u32 a, u32 d) 624 | { 625 | iolog("w16", a, d, 16); 626 | if ((a & 0xfff00000) == 0x7f000000) { 627 | u32 a_ = a & 0xffff; 628 | switch (a_) { 629 | case 0x28da: { 630 | int mode; 631 | mmsp2.mlc_stl_cntl = d | 0xaa; 632 | mode = (d >> 9) & 3; 633 | mmsp2.v.bpp = mode ? mode * 8 : 4; 634 | break; 635 | } 636 | case 0x290c: 637 | mmsp2.v.stride = d; 638 | return; 639 | case 0x290e: 640 | case 0x2910: 641 | // odd addresses don't affect LCD. What about TV? 642 | return; 643 | case 0x2912: 644 | mmsp2.mlc_stl_adrl = d; 645 | return; 646 | case 0x2914: 647 | mmsp2.mlc_stl_adrh = d; 648 | if (mmsp2.mlc_stl_adr != mmsp2.old_mlc_stl_adr) { 649 | // ask for refresh 650 | fb_sync_thread_futex = 1; 651 | g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL); 652 | } 653 | mmsp2.old_mlc_stl_adr = mmsp2.mlc_stl_adr; 654 | return; 655 | case 0x2958: // MLC_STL_PALLT_A 656 | mmsp2.mlc_stl_pallt_a = d & 0x1ff; 657 | return; 658 | case 0x295a: // MLC_STL_PALLT_D 659 | mmsp2.mlc_stl_pallt_d[mmsp2.mlc_stl_pallt_a++] = d; 660 | mmsp2.mlc_stl_pallt_a &= 0x1ff; 661 | mmsp2.v.dirty_pal = DIRTY_PAL_MMSP2; 662 | return; 663 | } 664 | } 665 | iolog_unh("w16", a, d, 16); 666 | } 667 | 668 | static void xwrite32(u32 a, u32 d) 669 | { 670 | iolog("w32", a, d, 32); 671 | 672 | if ((a & 0xfff00000) == 0x7f000000) { 673 | u32 a_ = a & 0xffff; 674 | switch (a_) { 675 | // GP2X 676 | case 0x295a: // MLC_STL_PALLT_D 677 | // special unaligned 32bit write, allegro seems to rely on it 678 | mmsp2.mlc_stl_pallt_d[mmsp2.mlc_stl_pallt_a++ & 0x1ff] = d; 679 | mmsp2.mlc_stl_pallt_d[mmsp2.mlc_stl_pallt_a++ & 0x1ff] = d >> 16; 680 | mmsp2.mlc_stl_pallt_a &= 0x1ff; 681 | mmsp2.v.dirty_pal = DIRTY_PAL_MMSP2; 682 | return; 683 | // Wiz 684 | case 0x4024: // MLCCONTROL0 685 | case 0x4058: // MLCCONTROL1 686 | pollux.mlccontrol = d; 687 | if (!(d & 0x20)) 688 | return; // layer not enabled 689 | if ((d >> 16) == 0x443A) 690 | pollux.v.bpp = 8; 691 | else 692 | pollux.v.bpp = 16; 693 | return; 694 | case 0x402c: // MLCVSTRIDE0 695 | case 0x4060: // MLCVSTRIDE1 696 | pollux.v.stride = d; 697 | return; 698 | case 0x4038: // MLCADDRESS0 699 | case 0x406c: // MLCADDRESS1 700 | pollux.mlc_stl_adr = d; 701 | if (d != mmsp2.old_mlc_stl_adr) { 702 | // ask for refresh 703 | fb_sync_thread_futex = 1; 704 | g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL); 705 | } 706 | mmsp2.old_mlc_stl_adr = d; 707 | return; 708 | case 0x403c: // MLCPALETTE0 709 | case 0x4070: // MLCPALETTE1 710 | pollux.mlcpalette[d >> 24] = d; 711 | pollux.v.dirty_pal = DIRTY_PAL_POLLUX; 712 | return; 713 | } 714 | } 715 | if ((a & 0xfff00000) == 0x7f100000) { 716 | u32 *bl = &blitter.dstctrl; 717 | u32 a_ = a & 0xfff; 718 | if (a_ < 0x40) { 719 | bl[a_ / 4] = d; 720 | if (a_ == 0x34 && (d & 1)) 721 | blitter_do(); 722 | return; 723 | } 724 | } 725 | iolog_unh("w32", a, d, 32); 726 | } 727 | 728 | #define LINKPAGE_SIZE 0x1000 729 | #define LINKPAGE_COUNT 4 730 | #define LINKPAGE_ALLOC (LINKPAGE_SIZE * LINKPAGE_COUNT) 731 | 732 | struct op_context { 733 | u32 pc; 734 | u32 op; 735 | u32 code[0]; 736 | }; 737 | 738 | struct op_linkpage { 739 | u32 *code_ptr; 740 | void (*handler)(struct op_context *op_ctx); 741 | u32 code[0]; 742 | }; 743 | 744 | struct op_stackframe { 745 | u32 saved_regs[15]; 746 | u32 cpsr; 747 | }; 748 | 749 | static struct op_linkpage *g_linkpages[2]; 750 | static int g_linkpage_count; 751 | 752 | enum opcond { 753 | C_EQ, C_NE, C_CS, C_CC, C_MI, C_PL, C_VS, C_VC, 754 | C_HI, C_LS, C_GE, C_LT, C_GT, C_LE, C_AL, 755 | }; 756 | enum cpsr_cond { 757 | CPSR_N = (1u << 31), 758 | CPSR_Z = (1u << 30), 759 | CPSR_C = (1u << 29), 760 | CPSR_V = (1u << 28), 761 | }; 762 | 763 | #define BIT_SET(v, b) (v & (1 << (b))) 764 | 765 | void emu_handle_op(struct op_context *op_ctx, struct op_stackframe *sframe) 766 | { 767 | u32 *regs = sframe->saved_regs; 768 | u32 cpsr = sframe->cpsr; 769 | u32 op = op_ctx->op; 770 | u32 t, shift, ret, addr; 771 | int i, rn, rd, cond; 772 | 773 | cond = (op & 0xf0000000) >> 28; 774 | rd = (op & 0x0000f000) >> 12; 775 | rn = (op & 0x000f0000) >> 16; 776 | 777 | if (cond != 0x0e) { 778 | switch (cond) { 779 | case C_EQ: if ( (cpsr & CPSR_Z)) break; return; 780 | case C_NE: if (!(cpsr & CPSR_Z)) break; return; 781 | case C_CS: if ( (cpsr & CPSR_C)) break; return; 782 | case C_CC: if (!(cpsr & CPSR_C)) break; return; 783 | case C_MI: if ( (cpsr & CPSR_N)) break; return; 784 | case C_PL: if (!(cpsr & CPSR_N)) break; return; 785 | case C_VS: if ( (cpsr & CPSR_V)) break; return; 786 | case C_VC: if (!(cpsr & CPSR_V)) break; return; 787 | default: 788 | goto unhandled; 789 | } 790 | } 791 | 792 | if ((op & 0x0f200090) == 0x01000090) { // AM3: LDRH, STRH 793 | if (!BIT_SET(op, 5)) // !H 794 | goto unhandled; 795 | if (BIT_SET(op, 6) && !BIT_SET(op, 20)) // S && !L 796 | goto unhandled; 797 | 798 | if (BIT_SET(op, 22)) // imm offset 799 | t = ((op & 0xf00) >> 4) | (op & 0x0f); 800 | else // reg offset 801 | t = regs[op & 0x000f]; 802 | 803 | if (!BIT_SET(op, 23)) 804 | t = -t; 805 | addr = regs[rn] + t; 806 | 807 | if (BIT_SET(op, 20)) { // Load 808 | ret = xread16(addr); 809 | if (BIT_SET(op, 6)) { // S 810 | ret <<= 16; 811 | ret = (signed int)ret >> 16; 812 | } 813 | regs[rd] = ret; 814 | } 815 | else 816 | xwrite16(addr, regs[rd]); 817 | } 818 | else if ((op & 0x0c000000) == 0x04000000) { // load/store word/byte 819 | if (BIT_SET(op, 21)) 820 | goto unhandled; // unprivileged 821 | if (BIT_SET(op, 25)) { // reg offs 822 | if (BIT_SET(op, 4)) 823 | goto unhandled; // nah it's media 824 | 825 | t = regs[op & 0x000f]; 826 | shift = (op & 0x0f80) >> 7; 827 | switch ((op & 0x0060) >> 5) { 828 | case 0: t = t << shift; break; 829 | case 1: t = t >> (shift + 1); break; 830 | case 2: t = (signed int)t >> (shift + 1); break; 831 | case 3: goto unhandled; // I'm just lazy 832 | } 833 | } 834 | else // imm offs 835 | t = op & 0x0fff; 836 | 837 | if (!BIT_SET(op, 23)) 838 | t = -t; 839 | 840 | addr = regs[rn]; 841 | if (BIT_SET(op, 24)) // pre-indexed 842 | addr += t; 843 | if (!BIT_SET(op, 24) || BIT_SET(op, 21)) 844 | regs[rn] += t; // writeback 845 | 846 | if (BIT_SET(op, 20)) { // Load 847 | if (BIT_SET(op, 22)) // Byte 848 | ret = xread8(addr); 849 | else 850 | ret = xread32(addr); 851 | regs[rd] = ret; 852 | } 853 | else { 854 | if (BIT_SET(op, 22)) // Byte 855 | xwrite8(addr, regs[rd]); 856 | else 857 | xwrite32(addr, regs[rd]); 858 | } 859 | } 860 | else 861 | goto unhandled; 862 | 863 | #if 0 864 | if (addr != addr_check) { 865 | fprintf(stderr, "bad calculated addr: %08x vs %08x\n", addr, addr_check); 866 | abort(); 867 | } 868 | #endif 869 | return; 870 | 871 | unhandled: 872 | err("unhandled IO op %08x @ %08x\n", op, op_ctx->pc); 873 | for (i = 0; i < 8-1; i++) 874 | err(" r%d=%08x r%-2d=%08x\n", i, regs[i], i+8, regs[i+8]); 875 | err(" r%d=%08x cpsr=%08x\n", i, regs[i], cpsr); 876 | abort(); 877 | } 878 | 879 | static u32 make_offset12(u32 *pc, u32 *target) 880 | { 881 | int lp_offs, u = 1; 882 | 883 | lp_offs = (char *)target - (char *)pc - 2*4; 884 | if (lp_offs < 0) { 885 | lp_offs = -lp_offs; 886 | u = 0; 887 | } 888 | if (lp_offs >= LINKPAGE_SIZE) { 889 | err("linkpage too far: %d\n", lp_offs); 890 | abort(); 891 | } 892 | 893 | return (u << 23) | lp_offs; 894 | } 895 | 896 | static u32 make_jmp(u32 *pc, u32 *target, int bl) 897 | { 898 | int jmp_val; 899 | 900 | jmp_val = target - pc - 2; 901 | if (jmp_val < (int)0xff800000 || jmp_val > 0x007fffff) { 902 | err("jump out of range (%p -> %p)\n", pc, target); 903 | abort(); 904 | } 905 | 906 | return 0xea000000 | (bl << 24) | (jmp_val & 0x00ffffff); 907 | } 908 | 909 | static void emit_op(struct op_linkpage *linkpage, u32 op) 910 | { 911 | *linkpage->code_ptr++ = op; 912 | } 913 | 914 | static void emit_op_io(struct op_linkpage *linkpage, 915 | u32 op, u32 *target) 916 | { 917 | op |= make_offset12(linkpage->code_ptr, target); 918 | emit_op(linkpage, op); 919 | } 920 | 921 | static void init_linkpage(struct op_linkpage *linkpage) 922 | { 923 | linkpage->handler = emu_call_handle_op; 924 | linkpage->code_ptr = linkpage->code; 925 | } 926 | 927 | static void segv_sigaction(int num, siginfo_t *info, void *ctx) 928 | { 929 | extern char _init, _end; 930 | struct ucontext *context = ctx; 931 | u32 *regs = (u32 *)&context->uc_mcontext.arm_r0; 932 | u32 *pc = (u32 *)regs[15]; 933 | u32 self_start, self_end; 934 | struct op_linkpage *lp = NULL; 935 | struct op_context *op_ctx; 936 | int i, ret, lp_i, lp_size; 937 | 938 | self_start = (u32)&_init & ~0xfff; 939 | self_end = (u32)&_end; 940 | 941 | if ((self_start <= regs[15] && regs[15] <= self_end) || // PC is in our segment or 942 | !((regs[15] ^ (u32)g_linkpages[0]) & ~(LINKPAGE_ALLOC - 1)) || // .. in linkpage 943 | ((long)info->si_addr & 0xffe00000) != 0x7f000000) // faulting not where expected 944 | { 945 | // real crash - time to die 946 | err("segv %d %p @ %08x\n", info->si_code, info->si_addr, regs[15]); 947 | for (i = 0; i < 8; i++) 948 | dbg(" r%d=%08x r%-2d=%08x\n", i, regs[i], i+8, regs[i+8]); 949 | signal(num, SIG_DFL); 950 | raise(num); 951 | return; 952 | } 953 | segvlog("segv %d %p @ %08x\n", info->si_code, info->si_addr, regs[15]); 954 | 955 | // find nearby linkpage 956 | for (lp_i = 0; lp_i < ARRAY_SIZE(g_linkpages); lp_i++) { 957 | if (g_linkpages[lp_i] == NULL) 958 | continue; 959 | i = g_linkpages[lp_i]->code_ptr + 2 - pc - 2; 960 | if ((int)0xff800000 <= i && i <= 0x007fffff) { 961 | lp = g_linkpages[lp_i]; 962 | break; 963 | } 964 | } 965 | 966 | if (lp == NULL) { 967 | err("fatal: no nearby linkpage for %08x\n", regs[15]); 968 | abort(); 969 | } 970 | 971 | if (emu_is_dl) { 972 | ret = mprotect((void *)((long)pc & ~0xfff), 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC); 973 | if (ret != 0) 974 | perror("warning: mprotect"); 975 | } 976 | 977 | // spit PC and op 978 | op_ctx = (void *)lp->code_ptr; 979 | op_ctx->pc = (u32)pc; 980 | op_ctx->op = *pc; 981 | lp->code_ptr = &op_ctx->code[0]; 982 | 983 | // emit jump to code ptr 984 | *pc = make_jmp(pc, lp->code_ptr, 0); 985 | 986 | // generate code: 987 | emit_op (lp, 0xe50d0000 + 0xf00 - 4 * 0); // str r0, [sp, #(-0xf00 + r0_offs)] 988 | emit_op (lp, 0xe50de000 + 0xf00 - 4 * 14); // str lr, [sp, #(-0xf00 + lr_offs)] 989 | emit_op (lp, 0xe24f0000 + (lp->code_ptr - (u32 *)op_ctx + 2) * 4); // sub r0, pc, #op_ctx 990 | emit_op (lp, 0xe1a0e00f); // mov lr, pc 991 | emit_op_io(lp, 0xe51ff000, (u32 *)&lp->handler); // ldr pc, =handle_op 992 | emit_op (lp, 0xe51de000 + 0xf00 - 4 * 14); // ldr lr, [sp, #(-0xf00 + lr_offs)] 993 | emit_op (lp, make_jmp(lp->code_ptr, pc + 1, 0)); // jmp 994 | 995 | // sync caches 996 | sys_cacheflush(pc, pc + 1); 997 | sys_cacheflush(lp, lp->code_ptr); 998 | 999 | lp_size = (char *)lp->code_ptr - (char *)lp; 1000 | segvlog("code #%d %d/%d\n", g_linkpage_count, lp_size, LINKPAGE_SIZE); 1001 | 1002 | if (lp_size + 14*4 > LINKPAGE_SIZE) { 1003 | g_linkpage_count++; 1004 | if (g_linkpage_count >= LINKPAGE_COUNT) { 1005 | err("too many linkpages needed\n"); 1006 | abort(); 1007 | } 1008 | g_linkpages[lp_i] = (void *)((char *)g_linkpages[lp_i] + LINKPAGE_SIZE); 1009 | init_linkpage(g_linkpages[lp_i]); 1010 | } 1011 | //handle_op(regs[15], op, regs, (u32)info->si_addr); 1012 | //regs[15] += 4; 1013 | } 1014 | 1015 | void emu_init(void *map_bottom[2], int is_dl) 1016 | { 1017 | sigaction_t segv_action = { 1018 | .sa_sigaction = segv_sigaction, 1019 | .sa_flags = SA_SIGINFO, 1020 | }; 1021 | const char *var; 1022 | void *pret; 1023 | int i, ret; 1024 | 1025 | #ifdef PND 1026 | if (geteuid() == 0) { 1027 | err("don't try to run as root, device registers or memory " 1028 | "might get trashed crashing the OS or even damaging the device.\n"); 1029 | exit(1); 1030 | } 1031 | #endif 1032 | 1033 | emu_is_dl = is_dl; 1034 | 1035 | #ifdef PND 1036 | // set default buffer size to 8 * 1K 1037 | emu_force_snd_frag = (8<<16) | 10; 1038 | #endif 1039 | var = getenv("GINGE_SETFRAGMENT"); 1040 | if (var != NULL) 1041 | emu_force_snd_frag = strtol(var, NULL, 0); 1042 | 1043 | for (i = 0; i < 2; i++) { 1044 | if (map_bottom[i] == NULL) 1045 | continue; 1046 | g_linkpages[i] = (void *)(((u32)map_bottom[i] - LINKPAGE_ALLOC) & ~0xfff); 1047 | pret = mmap(g_linkpages[i], LINKPAGE_ALLOC, PROT_READ|PROT_WRITE, 1048 | MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); 1049 | if (pret != g_linkpages[i]) { 1050 | err("linkpage alloc @ %p: ", g_linkpages[i]); 1051 | perror(NULL); 1052 | exit(1); 1053 | } 1054 | log("linkpages @ %p\n", g_linkpages[i]); 1055 | init_linkpage(g_linkpages[i]); 1056 | } 1057 | 1058 | // host stuff 1059 | ret = host_init(); 1060 | if (ret != 0) { 1061 | err("can't init host\n"); 1062 | exit(1); 1063 | } 1064 | 1065 | ret = host_video_init(NULL, 0); 1066 | if (ret != 0) { 1067 | err("can't init host video\n"); 1068 | exit(1); 1069 | } 1070 | 1071 | // TODO: check if this really fails on Wiz.. 1072 | mmsp2.umem = mmap(NULL, 0x2000000, PROT_READ|PROT_WRITE|PROT_EXEC, 1073 | MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 1074 | #ifdef WIZ 1075 | if (mmsp2.umem == MAP_FAILED) { 1076 | // we are short on memmory on Wiz, need special handling 1077 | extern void *host_mmap_upper(void); 1078 | mmsp2.umem = host_mmap_upper(); 1079 | } 1080 | #endif 1081 | if (mmsp2.umem == MAP_FAILED) { 1082 | perror(PFX "mmap upper mem"); 1083 | exit(1); 1084 | } 1085 | 1086 | pret = mmap(NULL, THREAD_STACK_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, 1087 | MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0); 1088 | if (mmsp2.umem == MAP_FAILED) { 1089 | perror(PFX "mmap thread stack"); 1090 | exit(1); 1091 | } 1092 | fb_sync_thread_futex = 1; 1093 | ret = g_clone(CLONE_VM | CLONE_FS | CLONE_FILES 1094 | | CLONE_SIGHAND | CLONE_THREAD, 1095 | (char *)pret + THREAD_STACK_SIZE, 0, 0, 0, 1096 | fb_sync_thread); 1097 | if (ret == 0 || ret == -1) { 1098 | perror(PFX "start fb thread"); 1099 | exit(1); 1100 | } 1101 | g_futex_raw(&fb_sync_thread_futex, FUTEX_WAIT, 1, NULL); 1102 | 1103 | // defaults 1104 | mmsp2.mlc_stl_adr = 0x03101000; // fb2 is at 0x03381000 1105 | mmsp2.mlc_stl_cntl = 0x4ab; // 16bpp, region 1 active 1106 | mmsp2.v.width = 320; 1107 | mmsp2.v.height = 240; 1108 | mmsp2.v.stride = 320*2; 1109 | mmsp2.v.bpp = 16; 1110 | mmsp2.v.dirty_pal = 1; 1111 | 1112 | sigemptyset(&segv_action.sa_mask); 1113 | sigaction(SIGSEGV, &segv_action, NULL); 1114 | } 1115 | 1116 | static long emu_mmap_dev(unsigned int length, int prot, int flags, unsigned int offset) 1117 | { 1118 | u8 *umem, *umem_end; 1119 | 1120 | // SoC regs 1121 | if ((offset & ~0x1ffff) == 0xc0000000) { 1122 | return g_mmap2_raw((void *)0x7f000000, length, PROT_NONE, 1123 | MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0); 1124 | } 1125 | // MMSP2 blitter 1126 | if ((offset & ~0xffff) == 0xe0020000) { 1127 | return g_mmap2_raw((void *)0x7f100000, length, PROT_NONE, 1128 | MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0); 1129 | } 1130 | // upper mem 1131 | if ((offset & 0xfe000000) != 0x02000000) { 1132 | err("unexpected devmem mmap @ %08x\n", offset); 1133 | return -EINVAL; 1134 | } 1135 | 1136 | umem = uppermem_lookup(offset, &umem_end); 1137 | if (umem + length > umem_end) 1138 | err("warning: uppermem @ %08x overflows by %d bytes\n", 1139 | offset, umem + length - umem_end); 1140 | 1141 | dbg("upper mem @ %08x %x = %p\n", offset, length, umem); 1142 | return (long)umem; 1143 | } 1144 | 1145 | long emu_do_mmap(unsigned int length, int prot, int flags, int fd, 1146 | unsigned int offset) 1147 | { 1148 | if (fd == FAKEDEV_MEM) 1149 | return emu_mmap_dev(length, prot, flags, offset); 1150 | 1151 | if (fd == FAKEDEV_FB0) 1152 | return emu_mmap_dev(length, prot, flags, offset + 0x03101000); 1153 | 1154 | if (fd == FAKEDEV_FB1) 1155 | return emu_mmap_dev(length, prot, flags, offset + 0x03381000); 1156 | 1157 | err("bad/ni mmap(?, %d, %x, %x, %d, %08x)\n", length, prot, flags, fd, offset); 1158 | return -EINVAL; 1159 | } 1160 | 1161 | long emu_do_munmap(void *addr, unsigned int length) 1162 | { 1163 | u8 *p = addr; 1164 | 1165 | // don't allow to unmap upper mem 1166 | if ((u8 *)mmsp2.umem <= p && p < (u8 *)mmsp2.umem + 0x2000000) { 1167 | dbg("ignoring munmap: %p %x\n", addr, length); 1168 | return 0; 1169 | } 1170 | 1171 | return -EAGAIN; 1172 | } 1173 | 1174 | static void emu_sound_open(int fd) 1175 | { 1176 | int ret; 1177 | 1178 | if (emu_force_snd_frag != 0) { 1179 | ret = g_ioctl_raw(fd, SNDCTL_DSP_SETFRAGMENT, &emu_force_snd_frag); 1180 | if (ret != 0) 1181 | err("snd ioctl SETFRAGMENT %08x: %d\n", emu_force_snd_frag, ret); 1182 | } 1183 | } 1184 | 1185 | static long emu_sound_ioctl(int fd, int request, void *argp) 1186 | { 1187 | int *arg = argp; 1188 | 1189 | #if 0 1190 | dbg("snd ioctl(%d, %08x, %p)", fd, request, argp); 1191 | if (arg != NULL) 1192 | dbg_c(" [%d]", *arg); 1193 | dbg_c("\n"); 1194 | #endif 1195 | 1196 | /* People set strange frag settings on GP2X, which even manage 1197 | * to break audio on pandora (causes writes to fail). 1198 | * Catch this and set to something that works. */ 1199 | switch(request) { 1200 | case SNDCTL_DSP_SETFRAGMENT: { 1201 | int bsize, frag, frag_cnt; 1202 | long ret; 1203 | 1204 | if (arg == NULL) 1205 | break; 1206 | 1207 | frag = *arg & 0xffff; 1208 | frag_cnt = *arg >> 16; 1209 | bsize = frag_cnt << frag; 1210 | if (frag < 8 || bsize < 4096*4 || bsize > 4096*4*2) { 1211 | /* 1212 | * ~4ms. gpSP wants small buffers or else it stutters 1213 | * because of it's audio thread sync stuff 1214 | * XXX: hardcoding, as low samplerates will result in small fragment size, 1215 | * which itself causes ALSA stall and hangs the program. 1216 | * Also some apps change samplerate without reopening /dev/dsp, 1217 | * which causes ALSA to reject SNDCTL_DSP_SETFRAGMENT. 1218 | */ 1219 | bsize = 44100 / 250 * 4; 1220 | 1221 | for (frag = 0; bsize; bsize >>= 1, frag++) 1222 | ; 1223 | 1224 | frag_cnt = 8; 1225 | } 1226 | 1227 | frag |= frag_cnt << 16; 1228 | ret = g_ioctl_raw(fd, SNDCTL_DSP_SETFRAGMENT, &frag); 1229 | if (ret != 0) 1230 | err("snd ioctl SETFRAGMENT %08x: %ld\n", frag, ret); 1231 | // indicate success even if we fail (because of ALSA mostly), 1232 | // things like MikMod will bail out otherwise. 1233 | return 0; 1234 | } 1235 | case SNDCTL_DSP_SYNC: 1236 | // Franxis tends to use sync/write loops, bad idea under ALSA 1237 | return 0; 1238 | default: 1239 | break; 1240 | } 1241 | 1242 | return g_ioctl_raw(fd, request, argp); 1243 | } 1244 | 1245 | long emu_do_ioctl(int fd, int request, void *argp) 1246 | { 1247 | if (fd == emu_interesting_fds[IFD_SOUND].fd) 1248 | return emu_sound_ioctl(fd, request, argp); 1249 | 1250 | switch (fd) { 1251 | /* *********************** */ 1252 | case FAKEDEV_FB0: 1253 | case FAKEDEV_FB1: 1254 | if (argp == NULL) 1255 | goto fail; 1256 | 1257 | switch (request) { 1258 | case FBIOGET_FSCREENINFO: { 1259 | struct fb_fix_screeninfo *fix = argp; 1260 | 1261 | memset(fix, 0, sizeof(*fix)); 1262 | strcpy(fix->id, "mmsp2_RGB0"); 1263 | fix->type = FB_TYPE_PACKED_PIXELS; 1264 | fix->accel = FB_ACCEL_NONE; 1265 | fix->visual = FB_VISUAL_TRUECOLOR; 1266 | fix->line_length = 320*2; 1267 | fix->smem_start = (fd == FAKEDEV_FB0) ? 0x03101000 : 0x03381000; 1268 | fix->smem_len = 320*240*2; 1269 | return 0; 1270 | } 1271 | case FBIOGET_VSCREENINFO: { 1272 | struct fb_var_screeninfo *var = argp; 1273 | static const struct fb_bitfield fbb_red = { offset: 11, length: 5, }; 1274 | static const struct fb_bitfield fbb_green = { offset: 5, length: 6, }; 1275 | static const struct fb_bitfield fbb_blue = { offset: 0, length: 5, }; 1276 | 1277 | memset(var, 0, sizeof(*var)); 1278 | var->activate = FB_ACTIVATE_NOW; 1279 | var->xres = 1280 | var->xres_virtual = 320; 1281 | var->yres = 1282 | var->yres_virtual = 240; 1283 | var->width = 1284 | var->height = -1; 1285 | var->vmode = FB_VMODE_NONINTERLACED; 1286 | var->bits_per_pixel = 16; 1287 | var->red = fbb_red; 1288 | var->green = fbb_green; 1289 | var->blue = fbb_blue; 1290 | return 0; 1291 | } 1292 | case FBIOPUT_VSCREENINFO: { 1293 | struct fb_var_screeninfo *var = argp; 1294 | dbg(" put vscreen: %dx%d@%d\n", var->xres, var->yres, var->bits_per_pixel); 1295 | if (var->xres != 320 || var->yres != 240 || var->bits_per_pixel != 16) 1296 | return -1; 1297 | return 0; 1298 | } 1299 | } 1300 | 1301 | /* *********************** */ 1302 | case FAKEDEV_TTY: 1303 | case FAKEDEV_TTY0: 1304 | // fake tty0 to make GPH SDL happy 1305 | if (request == 0x4b46) // KDGKBENT 1306 | return -1; 1307 | return 0; 1308 | } 1309 | 1310 | fail: 1311 | err("bad/ni ioctl(%d, %08x, %p)\n", fd, request, argp); 1312 | return -EINVAL; 1313 | } 1314 | 1315 | static const char wm97xx_p[] = 1316 | "5507 0 -831476 0 -4218 16450692 65536"; // from 4.0 fw 1317 | 1318 | long emu_do_read(int fd, void *buf, int count) 1319 | { 1320 | int ret, pressed = 0, x, y; 1321 | struct { 1322 | u16 pressure, x, y; 1323 | } wm97xx; 1324 | 1325 | if (count < 0) { 1326 | err("read(%d, %d)\n", fd, count); 1327 | return -EINVAL; 1328 | } 1329 | 1330 | switch (fd) { 1331 | case FAKEDEV_GPIO: 1332 | mmsp2.btn_state = host_read_btns(); 1333 | 1334 | if (count > 4) 1335 | count = 4; 1336 | memcpy(buf, &mmsp2.btn_state, count); 1337 | break; 1338 | case FAKEDEV_WM97XX: 1339 | ret = host_read_ts(&pressed, &x, &y); 1340 | if (ret == 0 && pressed) { 1341 | wm97xx.pressure = 0x8001; // TODO: check the real thing 1342 | wm97xx.x = x * 3750 / 1024 + 200; 1343 | wm97xx.y = 3750 - y * 3750 / 1024 + 200; 1344 | } 1345 | else { 1346 | wm97xx.pressure = 0; 1347 | wm97xx.x = wm97xx.y = 200; 1348 | } 1349 | 1350 | if (count > sizeof(wm97xx)) 1351 | count = sizeof(wm97xx); 1352 | memcpy(buf, &wm97xx, count); 1353 | break; 1354 | case FAKEDEV_WM97XX_P: 1355 | if (count < sizeof(wm97xx_p)) 1356 | err("incomplete pointercal read\n"); 1357 | strncpy(buf, wm97xx_p, count); 1358 | break; 1359 | default: 1360 | dbg("read(%d, %d)\n", fd, count); 1361 | return -EINVAL; 1362 | } 1363 | return count; 1364 | } 1365 | 1366 | struct dev_fd_t emu_interesting_fds[] = { 1367 | [IFD_SOUND] = { "/dev/dsp", -1, emu_sound_open }, 1368 | { NULL, 0, NULL }, 1369 | }; 1370 | 1371 | static const struct { 1372 | const char *from; 1373 | const char *to; 1374 | } path_map[] = { 1375 | { "/mnt/tmp", "./tmp" }, 1376 | { "/mnt/sd", "./mntsd" }, 1377 | }; 1378 | 1379 | const char *emu_wrap_path(const char *path) 1380 | { 1381 | char *buff, *p; 1382 | size_t size; 1383 | int i, len; 1384 | long ret; 1385 | 1386 | // do only path mapping for now 1387 | for (i = 0; i < ARRAY_SIZE(path_map); i++) { 1388 | p = strstr(path, path_map[i].from); 1389 | if (p != NULL) { 1390 | size = strlen(path) + strlen(path_map[i].to) + 1; 1391 | buff = malloc(size); 1392 | if (buff == NULL) 1393 | break; 1394 | len = p - path; 1395 | strncpy(buff, path, len); 1396 | snprintf(buff + len, size - len, "%s%s", path_map[i].to, 1397 | path + len + strlen(path_map[i].from)); 1398 | dbg("mapped path \"%s\" -> \"%s\"\n", path, buff); 1399 | 1400 | ret = g_mkdir_raw(path_map[i].to, 0666); 1401 | if (ret != 0 && ret != -EEXIST) 1402 | err("mkdir(%s): %ld\n", path_map[i].to, ret); 1403 | 1404 | return buff; 1405 | } 1406 | } 1407 | 1408 | return path; 1409 | } 1410 | 1411 | void emu_wrap_path_free(const char *w_path, const char *old_path) 1412 | { 1413 | if (w_path != old_path) 1414 | free((void *)w_path); 1415 | } 1416 | 1417 | void *emu_do_fopen(const char *path, const char *mode) 1418 | { 1419 | const char *w_path; 1420 | FILE *ret; 1421 | 1422 | if (strcmp(path, "/etc/pointercal") == 0) { 1423 | // use local pontercal, not host's 1424 | ret = fopen("pointercal", mode); 1425 | if (ret == NULL) { 1426 | ret = fopen("pointercal", "w"); 1427 | if (ret != NULL) { 1428 | fwrite(wm97xx_p, 1, sizeof(wm97xx_p), ret); 1429 | fclose(ret); 1430 | } 1431 | ret = fopen("pointercal", mode); 1432 | } 1433 | } 1434 | else { 1435 | w_path = emu_wrap_path(path); 1436 | ret = fopen(w_path, mode); 1437 | emu_wrap_path_free(w_path, path); 1438 | } 1439 | 1440 | return ret; 1441 | } 1442 | 1443 | // FIXME: threads.. 1444 | int emu_do_system(const char *command) 1445 | { 1446 | static char tmp_path[512]; 1447 | int need_ginge = 0; 1448 | const char *p2; 1449 | char *p; 1450 | int ret; 1451 | 1452 | if (command == NULL) 1453 | return -1; 1454 | 1455 | for (p2 = command; *p2 && isspace(*p2); p2++) 1456 | ; 1457 | 1458 | if (*p2 == '.') // relative path? 1459 | need_ginge = 1; 1460 | else if (*p2 == '/' && strncmp(p2, "/bin", 4) && strncmp(p2, "/lib", 4) 1461 | && strncmp(p2, "/sbin", 4) && strncmp(p2, "/usr", 4)) 1462 | // absolute path, but not a system command 1463 | need_ginge = 1; 1464 | 1465 | p2 = emu_wrap_path(command); 1466 | if (need_ginge) { 1467 | make_local_path(tmp_path, sizeof(tmp_path), "ginge_prep"); 1468 | p = tmp_path + strlen(tmp_path); 1469 | 1470 | snprintf(p, sizeof(tmp_path) - (p - tmp_path), " --nomenu %s", p2); 1471 | } 1472 | else 1473 | snprintf(tmp_path, sizeof(tmp_path), "%s", p2); 1474 | emu_wrap_path_free(p2, command); 1475 | 1476 | dbg("system: \"%s\"\n", tmp_path); 1477 | 1478 | // the app might want the screen too.. 1479 | fb_thread_pause(); 1480 | ret = system(tmp_path); 1481 | fb_thread_resume(); 1482 | return ret; 1483 | } 1484 | 1485 | long emu_do_execve(const char *filename, char * const argv[], 1486 | char * const envp[]) 1487 | { 1488 | const char **new_argv; 1489 | char *prep_path; 1490 | int i, argc; 1491 | long ret; 1492 | 1493 | if (filename == NULL) 1494 | return -1; 1495 | 1496 | if (strstr(filename, "gp2xmenu") != NULL) 1497 | host_forced_exit(0); 1498 | 1499 | for (i = 0; argv[i] != NULL; i++) 1500 | ; 1501 | argc = i + 1; 1502 | 1503 | new_argv = calloc(argc + 2, sizeof(new_argv[0])); 1504 | if (new_argv == NULL) 1505 | return -1; 1506 | 1507 | prep_path = malloc(512); 1508 | if (prep_path == NULL) 1509 | return -1; 1510 | 1511 | make_local_path(prep_path, 512, "ginge_prep"); 1512 | new_argv[0] = prep_path; 1513 | new_argv[1] = "--nomenu"; 1514 | new_argv[2] = emu_wrap_path(filename); 1515 | 1516 | if (argv[0] != NULL) 1517 | for (i = 1; argv[i] != NULL; i++) 1518 | new_argv[i + 2] = argv[i]; 1519 | 1520 | dbg("execve \"%s\" %s \"%s\"\n", new_argv[0], new_argv[1], new_argv[2]); 1521 | ret = execve(new_argv[0], (char **)new_argv, envp); 1522 | err("execve(%s): %ld\n", new_argv[0], ret); 1523 | return ret; 1524 | } 1525 | 1526 | // vim:shiftwidth=2:expandtab 1527 | -------------------------------------------------------------------------------- /loader/emu_arm.s: -------------------------------------------------------------------------------- 1 | @ vim:filetype=armasm 2 | .text 3 | 4 | @ save registers and flags and call emu_handle_op 5 | @ r0 and r14 must be saved by caller, r0 is arg for handle_op 6 | @ on return everything is restored except lr, which is used to return 7 | 8 | .globl emu_call_handle_op 9 | emu_call_handle_op: 10 | str sp, [sp, #(-0xf00 + 4*13)] 11 | sub sp, sp, #0xf00 12 | add sp, sp, #4 13 | stmia sp, {r1-r12} 14 | sub sp, sp, #4 15 | mrs r2, cpsr 16 | mov r1, sp 17 | str r2, [sp, #4*15] 18 | mov r4, lr 19 | bl emu_handle_op 20 | ldr r1, [sp, #4*15] 21 | mov lr, r4 22 | msr cpsr_f, r1 23 | ldmia sp, {r0-r13} 24 | bx lr 25 | 26 | -------------------------------------------------------------------------------- /loader/ginge_dyn.symver: -------------------------------------------------------------------------------- 1 | { 2 | global: 3 | open; 4 | fopen; 5 | mmap; 6 | mmap2; 7 | munmap; 8 | read; 9 | ioctl; 10 | sigaction; 11 | tcgetattr; 12 | tcsetattr; 13 | system; 14 | execl; 15 | execlp; 16 | execle; 17 | execv; 18 | execvp; 19 | execve; 20 | chdir; 21 | readlink; 22 | 23 | local: 24 | *; 25 | }; 26 | -------------------------------------------------------------------------------- /loader/header.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_sQt5fY5eUJn5tKV0IBTDxK0zqQutTqTp 2 | #define INCLUDE_sQt5fY5eUJn5tKV0IBTDxK0zqQutTqTp 1 3 | 4 | #include "llibc.h" 5 | 6 | #define PFX "ginge: " 7 | #define err(f, ...) g_fprintf(2, PFX f, ##__VA_ARGS__) 8 | #define log(f, ...) g_fprintf(1, PFX f, ##__VA_ARGS__) 9 | #ifdef DBG 10 | #define dbg log 11 | #define dbg_c printf 12 | #else 13 | #define dbg(...) 14 | #define dbg_c(...) 15 | #endif 16 | 17 | void do_entry(unsigned long entry, void *stack_frame, int stack_frame_cnt, void *exitf); 18 | 19 | struct dev_fd_t { 20 | const char *name; 21 | int fd; 22 | void (*open_cb)(int fd); 23 | }; 24 | extern struct dev_fd_t emu_interesting_fds[]; 25 | enum { 26 | IFD_SOUND = 0, 27 | }; 28 | 29 | // note: __FD_SETSIZE is 1024, fd_set stuff will crash if made larger 30 | enum { 31 | FAKEDEV_MEM = 1001, 32 | FAKEDEV_GPIO, 33 | FAKEDEV_FB0, 34 | FAKEDEV_FB1, 35 | FAKEDEV_MMUHACK, // 1005 36 | FAKEDEV_WM97XX, 37 | FAKEDEV_WM97XX_P, 38 | FAKEDEV_TTY, // 1008 39 | FAKEDEV_TTY0, 40 | FAKEDEV_FD_BOUNDARY, 41 | }; 42 | 43 | void do_patches(void *ptr, unsigned int size); 44 | 45 | struct op_context; 46 | 47 | void emu_init(void *map_bottom[2], int is_dl); 48 | void emu_call_handle_op(struct op_context *op_ctx); 49 | long emu_do_mmap(unsigned int length, int prot, int flags, int fd, unsigned int offset); 50 | long emu_do_munmap(void *addr, unsigned int length); 51 | long emu_do_ioctl(int fd, int request, void *argp); 52 | long emu_do_read(int fd, void *buf, int count); 53 | void *emu_do_fopen(const char *path, const char *mode); 54 | int emu_do_system(const char *command); 55 | long emu_do_execve(const char *filename, char *const argv[], char *const envp[]); 56 | const char *emu_wrap_path(const char *path); 57 | void emu_wrap_path_free(const char *w_path, const char *old_path); 58 | 59 | int host_init(void); 60 | int host_read_btns(void); 61 | int host_read_ts(int *pressed, int *x1024, int *y1024); 62 | void host_forced_exit(int status); 63 | 64 | enum { GP2X_UP = 0, GP2X_LEFT = 2, GP2X_DOWN = 4, GP2X_RIGHT = 6, 65 | GP2X_START = 8, GP2X_SELECT = 9, GP2X_L = 10, GP2X_R = 11, 66 | GP2X_A = 12, GP2X_B = 13, GP2X_X = 14, GP2X_Y = 15, 67 | GP2X_VOL_UP = 16, GP2X_VOL_DOWN = 17, GP2X_PUSH = 18 }; 68 | 69 | extern char *bin_path; 70 | extern char **g_argv; 71 | 72 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 73 | 74 | #endif /* INCLUDE_sQt5fY5eUJn5tKV0IBTDxK0zqQutTqTp */ 75 | -------------------------------------------------------------------------------- /loader/host.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011,2015 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | #define _GNU_SOURCE 1 // for plat.c 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../common/libpicofe/input.h" 19 | #include "../common/libpicofe/linux/in_evdev.h" 20 | #include "../common/host_fb.h" 21 | 22 | #include "header.h" 23 | #include "realfuncs.h" 24 | 25 | // must be affected by realfuncs.h 26 | #include "../common/libpicofe/input.c" 27 | #include "../common/libpicofe/linux/plat.c" 28 | #include "../common/libpicofe/linux/in_evdev.c" 29 | 30 | #ifdef PND 31 | #include "host_pnd.c" 32 | #elif defined(WIZ) 33 | #include "host_wiz.c" 34 | #endif 35 | 36 | char **g_argv; 37 | static int ts_fd = -1; 38 | 39 | /* touscreen. Could just use tsblib, but it's LGPL... */ 40 | static int tsc[7]; 41 | 42 | static int host_ts_init(void) 43 | { 44 | static const char name_def[] = "/dev/input/touchscreen0"; 45 | const char *name; 46 | FILE *f; 47 | int ret; 48 | 49 | f = fopen("/etc/pointercal", "r"); 50 | if (f == NULL) { 51 | perror("fopen pointercal"); 52 | return -1; 53 | } 54 | ret = fscanf(f, "%d %d %d %d %d %d %d", &tsc[0], &tsc[1], 55 | &tsc[2], &tsc[3], &tsc[4], &tsc[5], &tsc[6]); 56 | fclose(f); 57 | if (ret != 7) { 58 | fprintf(stderr, "could not parse pointercal\n"); 59 | return -1; 60 | } 61 | 62 | name = getenv("TSLIB_TSDEVICE"); 63 | if (name == NULL) 64 | name = name_def; 65 | ts_fd = open(name, O_RDONLY | O_NONBLOCK); 66 | if (ts_fd == -1) { 67 | fprintf(stderr, "open %s", name); 68 | perror(""); 69 | return -1; 70 | } 71 | return 0; 72 | } 73 | 74 | // returns ranges 0-1023 75 | int host_read_ts(int *pressure, int *x1024, int *y1024) 76 | { 77 | static int raw_x, raw_y, raw_p; 78 | struct input_event ev; 79 | ssize_t ret; 80 | 81 | if (ts_fd == -1) 82 | return -1; 83 | 84 | for (;;) { 85 | errno = 0; 86 | ret = read(ts_fd, &ev, sizeof(ev)); 87 | if (ret != sizeof(ev)) { 88 | if (errno == EAGAIN) 89 | break; 90 | perror("ts read"); 91 | return -1; 92 | } 93 | if (ev.type == EV_ABS) { 94 | switch (ev.code) { 95 | case ABS_X: raw_x = ev.value; break; 96 | case ABS_Y: raw_y = ev.value; break; 97 | case ABS_PRESSURE: raw_p = ev.value; break; 98 | } 99 | } 100 | } 101 | 102 | *pressure = raw_p; 103 | *x1024 = (tsc[0] * raw_x + tsc[1] * raw_y + tsc[2]) / tsc[6]; 104 | *y1024 = (tsc[3] * raw_x + tsc[4] * raw_y + tsc[5]) / tsc[6]; 105 | 106 | host_video_normalize_ts(x1024, y1024); 107 | 108 | return 0; 109 | } 110 | 111 | int host_init(void) 112 | { 113 | in_init(); 114 | host_init_input(); 115 | in_probe(); 116 | host_ts_init(); 117 | 118 | return 0; 119 | } 120 | 121 | int host_read_btns(void) 122 | { 123 | int actions[IN_BINDTYPE_COUNT] = { 0, }; 124 | 125 | in_update(actions); 126 | host_actions(actions); 127 | 128 | return actions[IN_BINDTYPE_PLAYER12]; 129 | } 130 | 131 | void host_forced_exit(int status) 132 | { 133 | // exit() might not be enough because loader and app data is out of sync, 134 | // and other threads (which are really processes on this old glibc used) 135 | // might not exit properly. 136 | char cmd[64]; 137 | 138 | printf("forced exit...\n"); 139 | 140 | if (g_argv != NULL) { 141 | unsetenv("LD_PRELOAD"); 142 | unsetenv("LD_LIBRARY_PATH"); 143 | 144 | snprintf(cmd, sizeof(cmd), "killall %s", g_argv[0]); 145 | system(cmd); 146 | usleep(300000); 147 | snprintf(cmd, sizeof(cmd), "killall -9 %s", g_argv[0]); 148 | system(cmd); 149 | } 150 | exit(status); 151 | } 152 | 153 | // vim:shiftwidth=2:expandtab 154 | -------------------------------------------------------------------------------- /loader/host_pnd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011,2015 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | 8 | static struct in_default_bind in_evdev_defbinds[] = { 9 | { KEY_UP, IN_BINDTYPE_PLAYER12, GP2X_UP }, 10 | { KEY_LEFT, IN_BINDTYPE_PLAYER12, GP2X_LEFT }, 11 | { KEY_DOWN, IN_BINDTYPE_PLAYER12, GP2X_DOWN }, 12 | { KEY_RIGHT, IN_BINDTYPE_PLAYER12, GP2X_RIGHT }, 13 | { KEY_PAGEUP, IN_BINDTYPE_PLAYER12, GP2X_Y }, 14 | { KEY_END, IN_BINDTYPE_PLAYER12, GP2X_B }, 15 | { KEY_PAGEDOWN, IN_BINDTYPE_PLAYER12, GP2X_X }, 16 | { KEY_HOME, IN_BINDTYPE_PLAYER12, GP2X_A }, 17 | { KEY_RIGHTSHIFT, IN_BINDTYPE_PLAYER12, GP2X_L }, 18 | { KEY_RIGHTCTRL, IN_BINDTYPE_PLAYER12, GP2X_R }, 19 | { KEY_LEFTALT, IN_BINDTYPE_PLAYER12, GP2X_START }, 20 | { KEY_LEFTCTRL, IN_BINDTYPE_PLAYER12, GP2X_SELECT }, 21 | { KEY_COMMA, IN_BINDTYPE_PLAYER12, GP2X_VOL_DOWN }, 22 | { KEY_DOT, IN_BINDTYPE_PLAYER12, GP2X_VOL_UP }, 23 | { KEY_1, IN_BINDTYPE_PLAYER12, GP2X_PUSH }, 24 | { KEY_Q, IN_BINDTYPE_EMU, 0 }, 25 | { 0, 0, 0 }, 26 | }; 27 | 28 | static const struct in_pdata pandora_evdev_pdata = { 29 | .defbinds = in_evdev_defbinds, 30 | }; 31 | 32 | static void host_actions(int actions[IN_BINDTYPE_COUNT]) 33 | { 34 | if (actions[IN_BINDTYPE_EMU] & 1) 35 | host_forced_exit(1); 36 | } 37 | 38 | static void host_init_input(void) 39 | { 40 | in_evdev_init(&pandora_evdev_pdata); 41 | } 42 | 43 | // vim:shiftwidth=2:expandtab 44 | -------------------------------------------------------------------------------- /loader/host_wiz.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../common/warm/warm.h" 15 | 16 | extern int memdev, probably_caanoo; // leasing from wiz_video 17 | 18 | #define BTN_JOY BTN_JOYSTICK 19 | 20 | static struct in_default_bind wiz_evdev_defbinds[] = { 21 | { KEY_UP, IN_BINDTYPE_PLAYER12, GP2X_UP }, 22 | { KEY_DOWN, IN_BINDTYPE_PLAYER12, GP2X_DOWN }, 23 | { KEY_LEFT, IN_BINDTYPE_PLAYER12, GP2X_LEFT }, 24 | { KEY_RIGHT, IN_BINDTYPE_PLAYER12, GP2X_RIGHT }, 25 | { BTN_JOY + 0, IN_BINDTYPE_PLAYER12, GP2X_A }, 26 | { BTN_JOY + 1, IN_BINDTYPE_PLAYER12, GP2X_X }, 27 | { BTN_JOY + 2, IN_BINDTYPE_PLAYER12, GP2X_B }, 28 | { BTN_JOY + 3, IN_BINDTYPE_PLAYER12, GP2X_Y }, 29 | { BTN_JOY + 4, IN_BINDTYPE_PLAYER12, GP2X_L }, 30 | { BTN_JOY + 5, IN_BINDTYPE_PLAYER12, GP2X_R }, 31 | { BTN_JOY + 8, IN_BINDTYPE_PLAYER12, GP2X_START }, 32 | { BTN_JOY + 9, IN_BINDTYPE_PLAYER12, GP2X_SELECT }, 33 | { BTN_JOY + 10, IN_BINDTYPE_PLAYER12, GP2X_PUSH }, 34 | { BTN_JOY + 6, IN_BINDTYPE_EMU, 0 }, 35 | { 0, 0, 0 } 36 | }; 37 | 38 | static const struct in_pdata wiz_evdev_pdata = { 39 | .defbinds = wiz_evdev_defbinds, 40 | }; 41 | 42 | // todo: rm when generic code works on Wiz 43 | #if 0 44 | static int gpiodev = -1; 45 | 46 | int host_init(void) 47 | { 48 | gpiodev = open("/dev/GPIO", O_RDONLY); 49 | if (gpiodev < 0) 50 | perror(PFX "couldn't open /dev/GPIO"); 51 | 52 | return 0; 53 | } 54 | 55 | int host_read_btns(void) 56 | { 57 | int r, value = 0; 58 | 59 | r = read(gpiodev, &value, 4); 60 | if (value & 0x02) 61 | value |= 0x05; 62 | if (value & 0x08) 63 | value |= 0x14; 64 | if (value & 0x20) 65 | value |= 0x50; 66 | if (value & 0x80) 67 | value |= 0x41; 68 | 69 | return value; 70 | } 71 | #endif 72 | 73 | void *host_mmap_upper(void) 74 | { 75 | void *ret; 76 | int r; 77 | 78 | // make sure this never happens on Caanoo 79 | if (probably_caanoo) { 80 | err("Wiz mmap code called on Caanoo?"); 81 | return MAP_FAILED; 82 | } 83 | 84 | // Wiz GP2X 85 | // 03460000-03ffffff 00ba0000 86 | // 02aa0000-02dfffff 03100000-0345ffff 00360000 87 | // 03000000-030fffff 00100000 88 | // 03000000-03ffffff 02000000-02ffffff 01000000 89 | ret = mmap((void *)0x82000000, 0x1000000, PROT_READ|PROT_WRITE|PROT_EXEC, 90 | MAP_SHARED|MAP_FIXED, memdev, 0x3000000); 91 | if (ret != (void *)0x82000000) 92 | goto fail; 93 | 94 | ret = mmap((void *)0x83000000, 0x100000, PROT_READ|PROT_WRITE|PROT_EXEC, 95 | MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); 96 | if (ret != (void *)0x83000000) 97 | goto fail; 98 | 99 | ret = mmap((void *)0x83100000, 0x360000, PROT_READ|PROT_WRITE|PROT_EXEC, 100 | MAP_SHARED|MAP_FIXED, memdev, 0x2aa0000); 101 | if (ret != (void *)0x83100000) 102 | goto fail; 103 | 104 | ret = mmap((void *)0x83460000, 0xba0000, PROT_READ|PROT_WRITE|PROT_EXEC, 105 | MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); 106 | if (ret != (void *)0x83460000) 107 | goto fail; 108 | 109 | r = warm_change_cb_range(WCB_B_BIT|WCB_C_BIT, 1, (void *)0x82000000, 0x1000000); 110 | r |= warm_change_cb_range(WCB_B_BIT|WCB_C_BIT, 1, (void *)0x83100000, 0x360000); 111 | if (r != 0) 112 | err("could not make upper mem cacheable.\n"); 113 | 114 | return (void *)0x82000000; 115 | 116 | fail: 117 | err("mmap %p: ", ret); 118 | perror(NULL); 119 | exit(1); 120 | } 121 | 122 | static void host_actions(int actions[IN_BINDTYPE_COUNT]) 123 | { 124 | if (probably_caanoo && (actions[IN_BINDTYPE_EMU] & 1)) { 125 | // 'home key as Fn' handling 126 | int act = actions[IN_BINDTYPE_PLAYER12]; 127 | if (act & (1 << GP2X_START)) { 128 | act &= ~(1 << GP2X_START); 129 | act |= 1 << GP2X_VOL_UP; 130 | } 131 | if (act & (1 << GP2X_SELECT)) { 132 | act &= ~(1 << GP2X_SELECT); 133 | act |= 1 << GP2X_VOL_DOWN; 134 | } 135 | if (act & (1 << GP2X_Y)) 136 | host_forced_exit(1); 137 | actions[IN_BINDTYPE_PLAYER12] = act; 138 | } 139 | } 140 | 141 | static void host_init_input(void) 142 | { 143 | in_evdev_init(&wiz_evdev_pdata); 144 | } 145 | 146 | // vim:shiftwidth=2:expandtab 147 | -------------------------------------------------------------------------------- /loader/llibc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2016 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | #include 8 | #include 9 | #include 10 | 11 | #include "syscalls.h" 12 | #include "llibc.h" 13 | 14 | // lame, broken and slow, but enough for ginge's needs 15 | static void format_number(char **dst_, int dst_len, unsigned int n, 16 | char fmt, int justify, int zeropad) 17 | { 18 | char buf[32], *p = buf, *dst; 19 | int printing = 0; 20 | unsigned int div; 21 | unsigned int t; 22 | unsigned int w; 23 | int spaces; 24 | int neg = 0; 25 | int left; 26 | 27 | w = justify < 0 ? -justify : justify; 28 | if (w >= 32) 29 | w = 31; 30 | 31 | switch (fmt) { 32 | case 'i': 33 | case 'd': 34 | if ((signed int)n < 0) { 35 | n = -n; 36 | neg = 1; 37 | } 38 | case 'u': 39 | div = 1000000000; 40 | left = 10; 41 | while (w > left) { 42 | *p++ = ' '; 43 | w--; 44 | continue; 45 | } 46 | while (left > 0) { 47 | t = n / div; 48 | n -= t * div; 49 | div /= 10; 50 | if (t || left == 1) { 51 | if (neg && t && !printing) { 52 | *p++ = '-'; 53 | if (w > 0) w--; 54 | } 55 | printing = 1; 56 | } 57 | if (printing) 58 | *p++ = t + '0'; 59 | else if (w >= left) { 60 | *p++ = ' '; 61 | w--; 62 | } 63 | left--; 64 | } 65 | break; 66 | 67 | case 'p': 68 | w = 8; 69 | zeropad = 1; 70 | case 'x': 71 | left = 8; 72 | while (w > left) { 73 | *p++ = zeropad ? '0' : ' '; 74 | w--; 75 | continue; 76 | } 77 | while (left > 0) { 78 | t = n >> (left * 4 - 4); 79 | t &= 0x0f; 80 | if (t || left == 1) 81 | printing = 1; 82 | if (printing) 83 | *p++ = t < 10 ? t + '0' : t + 'a' - 10; 84 | else if (w >= left) { 85 | *p++ = zeropad ? '0' : ' '; 86 | w--; 87 | } 88 | left--; 89 | } 90 | break; 91 | 92 | default: 93 | memcpy(buf, "", 9); 94 | break; 95 | } 96 | *p = 0; 97 | 98 | spaces = 0; 99 | p = buf; 100 | if (justify < 0) { 101 | while (*p == ' ') { 102 | spaces++; 103 | p++; 104 | } 105 | } 106 | 107 | dst = *dst_; 108 | while (*p != 0 && dst_len > 1) { 109 | *dst++ = *p++; 110 | dst_len--; 111 | } 112 | while (spaces > 0 && dst_len > 1) { 113 | *dst++ = ' '; 114 | spaces--; 115 | dst_len--; 116 | } 117 | *dst = 0; 118 | *dst_ = dst; 119 | } 120 | 121 | int parse_dec(const char **p_) 122 | { 123 | const char *p = *p_; 124 | int neg = 0; 125 | int r = 0; 126 | 127 | if (*p == '-') { 128 | neg = 1; 129 | p++; 130 | } 131 | 132 | while ('0' <= *p && *p <= '9') { 133 | r = r * 10 + *p - '0'; 134 | p++; 135 | } 136 | 137 | *p_ = p; 138 | return neg ? -r : r; 139 | } 140 | 141 | void g_fprintf(int fd, const char *fmt, ...) 142 | { 143 | char buf[256], *d = buf; 144 | const char *s = fmt; 145 | int left = sizeof(buf);; 146 | int justify; 147 | int zeropad; 148 | va_list ap; 149 | 150 | va_start(ap, fmt); 151 | while (*s != 0 && left > 1) { 152 | if (*s != '%') { 153 | *d++ = *s++; 154 | left--; 155 | continue; 156 | } 157 | s++; 158 | if (*s == 0) 159 | break; 160 | if (*s == '%') { 161 | *d++ = *s++; 162 | left--; 163 | continue; 164 | } 165 | 166 | zeropad = *s == '0'; 167 | justify = parse_dec(&s); 168 | if (*s == 'l' || *s == 'z') 169 | s++; // ignore for now 170 | if (*s == 's') { 171 | const char *ns = va_arg(ap, const char *); 172 | if (ns == NULL) 173 | ns = "(null)"; 174 | int len = strlen(ns); 175 | while (justify > len && left > 1) { 176 | *d++ = ' '; 177 | justify--; 178 | left--; 179 | } 180 | if (len > left - 1) { 181 | memcpy(d, ns, left - 1); 182 | break; 183 | } 184 | memcpy(d, ns, len); 185 | d += len; 186 | left -= len; 187 | while (justify < -len && left > 1) { 188 | *d++ = ' '; 189 | justify++; 190 | left--; 191 | } 192 | s++; 193 | continue; 194 | } 195 | 196 | format_number(&d, left, va_arg(ap, int), *s++, justify, zeropad); 197 | } 198 | *d = 0; 199 | va_end(ap); 200 | 201 | g_write_raw(fd, buf, d - buf); 202 | } 203 | 204 | // vim:shiftwidth=2:expandtab 205 | -------------------------------------------------------------------------------- /loader/llibc.h: -------------------------------------------------------------------------------- 1 | void g_fprintf(int fd, const char *fmt, ...) 2 | __attribute__((format(printf, 2, 3))); 3 | 4 | #define g_printf(fmt, ...) \ 5 | g_fprintf(1, fmt, ##__VA_ARGS__) 6 | -------------------------------------------------------------------------------- /loader/loader.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "header.h" 17 | #include "realfuncs.h" 18 | #include "syscalls.h" 19 | 20 | char *bin_path; 21 | char **g_argv; 22 | 23 | #define CHECK_(val, fail_operator, expect, err_msg) \ 24 | if (val fail_operator expect) { \ 25 | fprintf(stderr, err_msg ", exiting (%d)\n", (int)(long)val); \ 26 | return 1; \ 27 | } 28 | 29 | #define CHECK_EQ(val, expect, err_msg) \ 30 | CHECK_(val, !=, expect, err_msg) 31 | 32 | #define CHECK_NE(val, expect, err_msg) \ 33 | CHECK_(val, ==, expect, err_msg) 34 | 35 | #define HDR_CHECK_EQ(field, expect, err_msg) \ 36 | CHECK_(hdr.field, !=, expect, err_msg) 37 | 38 | #define HDR_CHECK_NE(field, expect, err_msg) \ 39 | CHECK_(hdr.field, ==, expect, err_msg) 40 | 41 | #define FAIL_PERROR(msg) { \ 42 | perror(msg); \ 43 | return 1; \ 44 | } 45 | 46 | typedef struct { 47 | unsigned long start; 48 | unsigned long end; 49 | } maps_range; 50 | 51 | static int is_range_used(maps_range *maps, int map_cnt, unsigned long start, unsigned long end) 52 | { 53 | int i; 54 | for (i = 0; i < map_cnt; i++) { 55 | if (maps[i].end <= start) 56 | continue; 57 | if (end <= maps[i].start) 58 | continue; 59 | 60 | return i + 1; 61 | } 62 | 63 | return 0; 64 | } 65 | 66 | extern char **environ; 67 | 68 | int main(int argc, char *argv[]) 69 | { 70 | void *lowest_segments[2] = { NULL, NULL }; 71 | Elf32_Ehdr hdr; 72 | Elf32_Phdr *phdr; 73 | FILE *fi; 74 | maps_range maps[16]; 75 | int map_cnt; 76 | int i, ret, envc, sfp; 77 | long *stack_frame; 78 | struct stat st; 79 | char buf[64]; 80 | long lret; 81 | 82 | if (argc < 2) { 83 | fprintf(stderr, "usage: %s [args]\n", argv[0]); 84 | return 1; 85 | } 86 | 87 | g_argv = argv; 88 | 89 | lret = g_personality(-1); 90 | if (g_syscall_error(lret) != -1) { 91 | lret |= 0x0240000; // ADDR_COMPAT_LAYOUT | ADDR_NO_RANDOMIZE 92 | g_personality(lret); 93 | } 94 | 95 | fi = fopen("/proc/self/maps", "r"); 96 | CHECK_NE(fi, NULL, "fopen maps"); 97 | 98 | for (i = 0; i < ARRAY_SIZE(maps); i++) { 99 | ret = fscanf(fi, "%lx-%lx %*s %*s %*s %*s %*s\n", &maps[i].start, &maps[i].end); 100 | if (ret <= 0) 101 | break; 102 | CHECK_EQ(ret, 2, "maps parse error"); 103 | } 104 | fclose(fi); 105 | map_cnt = i; 106 | CHECK_NE(map_cnt, 0, "no maps"); 107 | CHECK_NE(map_cnt, ARRAY_SIZE(maps), "too many maps"); 108 | 109 | fi = fopen(argv[1], "rb"); 110 | if (fi == NULL) 111 | FAIL_PERROR("fopen"); 112 | 113 | if (fread(&hdr, 1, sizeof(hdr), fi) != sizeof(hdr)) 114 | FAIL_PERROR("too small or"); 115 | 116 | if (memcmp(hdr.e_ident, ELFMAG "\x01\x01", SELFMAG + 2) != 0) { 117 | fprintf(stderr, "not 32bit LE ELF?\n"); 118 | return 1; 119 | } 120 | 121 | HDR_CHECK_EQ(e_type, ET_EXEC, "not executable"); 122 | HDR_CHECK_EQ(e_machine, EM_ARM, "not ARM"); 123 | HDR_CHECK_EQ(e_phentsize, sizeof(Elf32_Phdr), "bad PH entry size"); 124 | HDR_CHECK_NE(e_phnum, 0, "no PH entries"); 125 | 126 | phdr = malloc(hdr.e_phnum * hdr.e_phentsize); 127 | CHECK_NE(phdr, NULL, "OOM"); 128 | 129 | if (fread(phdr, hdr.e_phentsize, hdr.e_phnum, fi) != hdr.e_phnum) 130 | FAIL_PERROR("too small or"); 131 | 132 | for (i = 0; i < hdr.e_phnum; i++) { 133 | Elf32_Addr end_addr = phdr[i].p_vaddr + phdr[i].p_memsz; 134 | Elf32_Addr align; 135 | void *ptr, *map_ptr; 136 | 137 | if (phdr[i].p_type == PT_NOTE) 138 | continue; 139 | if (phdr[i].p_type != PT_LOAD) { 140 | fprintf(stderr, "skipping section %d\n", phdr[i].p_type); 141 | continue; 142 | } 143 | 144 | ret = is_range_used(maps, map_cnt, phdr[i].p_vaddr, end_addr); 145 | if (ret) { 146 | fprintf(stderr, "segment %d (%08x-%08x) hits %08lx-%08lx in maps\n", 147 | i, phdr[i].p_vaddr, end_addr, maps[ret - 1].start, maps[ret - 1].end); 148 | return 1; 149 | } 150 | 151 | log("load %d %08x-%08x from %08x\n", phdr[i].p_type, 152 | phdr[i].p_vaddr, end_addr, phdr[i].p_offset); 153 | 154 | align = phdr[i].p_vaddr & 0xfff; 155 | map_ptr = (void *)(phdr[i].p_vaddr - align); 156 | ptr = mmap(map_ptr, phdr[i].p_memsz + align, PROT_READ|PROT_WRITE|PROT_EXEC, 157 | MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 158 | if (ptr == MAP_FAILED || ptr != map_ptr) 159 | FAIL_PERROR("mmap"); 160 | 161 | if (phdr[i].p_filesz > 0) { 162 | if (fseek(fi, phdr[i].p_offset, SEEK_SET) != 0) 163 | FAIL_PERROR("fseek"); 164 | if (fread((char *)ptr + align, 1, phdr[i].p_filesz, fi) != phdr[i].p_filesz) 165 | FAIL_PERROR("too small or"); 166 | 167 | if (phdr[i].p_flags & PF_X) 168 | do_patches((char *)ptr + align, phdr[i].p_filesz); 169 | } 170 | 171 | if (lowest_segments[0] == NULL || map_ptr < lowest_segments[0]) 172 | lowest_segments[0] = map_ptr; 173 | } 174 | 175 | // build self bin path 176 | snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fileno(fi)); 177 | if (lstat(buf, &st) != 0) 178 | FAIL_PERROR("lstat bin_path"); 179 | bin_path = malloc(st.st_size + 1); 180 | CHECK_NE(bin_path, NULL, "bin_path"); 181 | ret = readlink(buf, bin_path, st.st_size); 182 | if (ret < 0) 183 | FAIL_PERROR("readlink"); 184 | bin_path[ret] = 0; 185 | 186 | fclose(fi); 187 | 188 | emu_init(lowest_segments, 0); 189 | 190 | // generate stack frame: argc, argv[], NULL, env[], NULL 191 | for (envc = 0; environ[envc] != NULL; envc++) 192 | ; 193 | 194 | stack_frame = calloc(argc + envc + 3, sizeof(stack_frame[0])); 195 | if (stack_frame == NULL) { 196 | fprintf(stderr, "stack_frame OOM\n"); 197 | return 1; 198 | } 199 | 200 | // update the environment 201 | setenv("_", bin_path, 1); 202 | 203 | sfp = 0; 204 | stack_frame[sfp++] = argc - 1; 205 | for (i = 1; i < argc; i++) 206 | stack_frame[sfp++] = (long)argv[i]; 207 | stack_frame[sfp++] = 0; 208 | for (i = 0; i < envc; i++) 209 | stack_frame[sfp++] = (long)environ[i]; 210 | stack_frame[sfp++] = 0; 211 | 212 | log("entering %08x, %d stack entries\n", hdr.e_entry, sfp); 213 | do_entry(hdr.e_entry, stack_frame, sfp, NULL); 214 | 215 | fprintf(stderr, "do_entry failed!\n"); 216 | return 1; 217 | } 218 | 219 | // vim:shiftwidth=2:expandtab 220 | -------------------------------------------------------------------------------- /loader/loader_arm.s: -------------------------------------------------------------------------------- 1 | @ vim:filetype=armasm 2 | 3 | .text 4 | 5 | /* void do_entry(Elf32_Addr entry, void *stack_frame, int stack_frame_elems, void *exitf); */ 6 | 7 | .globl do_entry 8 | do_entry: 9 | sub sp, sp, r2, lsl #2 10 | mov r4, sp 11 | mov r5, r0 12 | 0: 13 | ldr r0, [r1], #4 14 | subs r2, r2, #1 15 | str r0, [r4], #4 16 | bgt 0b 17 | 18 | /* 19 | r0 - atexit func 20 | sp - stack frame of: 21 | argc 22 | argv[0] 23 | ... 24 | NULL 25 | envp[0] 26 | ... 27 | NULL 28 | */ 29 | mov r0, r3 30 | bx r5 31 | -------------------------------------------------------------------------------- /loader/loader_ia32.s: -------------------------------------------------------------------------------- 1 | .text 2 | 3 | /* void do_entry(Elf32_Addr entry, void *stack_frame, int stack_frame_size, void *exitf); */ 4 | 5 | .globl do_entry 6 | do_entry: 7 | movl 4(%esp), %ebx 8 | movl 8(%esp), %esi 9 | movl 12(%esp), %eax 10 | movl 16(%esp), %edx 11 | 12 | movl %eax, %ecx 13 | shll $2, %ecx 14 | subl %ecx, %esp 15 | 16 | /* copy stack frame */ 17 | movl %esp, %edi 18 | 0: 19 | movl (%esi), %ecx 20 | movl %ecx, (%edi) 21 | addl $4, %esi 22 | addl $4, %edi 23 | subl $1, %eax 24 | jg 0b 25 | 26 | jmp *%ebx 27 | -------------------------------------------------------------------------------- /loader/mmsp2-regs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * include/asm-arm/arch-mmsp2/mmsp2-regs.h 3 | * 4 | * Copyright (C) 2004,2005 DIGNSYS Inc. (www.dignsys.com) 5 | * Kane Ahn < hbahn@dignsys.com > 6 | * hhsong < hhsong@dignsys.com > 7 | */ 8 | 9 | #ifndef _MMSP2_H 10 | #define _MMSP2_H 11 | 12 | static const char * const regnames[0x10000] = { 13 | 14 | /* 15 | * BANK C (Static) Memory Control Register 16 | */ 17 | [0x3a00] = "MEMCFG", /* BANK C configuration */ 18 | [0x3a02] = "MEMTIME0", /* BANK C Timing #0 */ 19 | [0x3a04] = "MEMTIME1", /* BANK C Timing #1 */ 20 | [0x3a06] = "MEMTIME2", /* BANK C Timing #2 */ 21 | [0x3a08] = "MEMTIME3", /* BANK C Timing #3 */ 22 | [0x3a0a] = "MEMTIME4", /* BANK C Timing #4 */ 23 | [0x3a0e] = "MEMWAITCTRL", /* BANK C Wait Control */ 24 | [0x3a10] = "MEMPAGE", /* BANK C Page Control */ 25 | [0x3a12] = "MEMIDETIME", /* BANK C IDE Timing Control */ 26 | 27 | [0x3a14] = "MEMPCMCIAM", /* BANK C PCMCIA Timing */ 28 | [0x3a16] = "MEMPCMCIAA", /* PCMCIA Attribute Timing */ 29 | [0x3a18] = "MEMPCMCIAI", /* PCMCIA I/O Timing */ 30 | [0x3a1a] = "MEMPCMCIAWAIT", /* PCMCIA Wait Timing */ 31 | 32 | [0x3a1c] = "MEMEIDEWAIT", /* IDE Wait Timing */ 33 | 34 | [0x3a20] = "MEMDTIMEOUT", /* DMA Timeout */ 35 | [0x3a22] = "MEMDMACTRL", /* DMA Control */ 36 | [0x3a24] = "MEMDMAPOL", /* DMA Polarity */ 37 | [0x3a26] = "MEMDMATIME0", /* DMA Timing #0 */ 38 | [0x3a28] = "MEMDMATIME1", /* DMA Timing #1 */ 39 | [0x3a2a] = "MEMDMATIME2", /* DMA Timing #2 */ 40 | [0x3a2c] = "MEMDMATIME3", /* DMA Timing #3 */ 41 | [0x3a2e] = "MEMDMATIME4", /* DMA Timing #4 */ 42 | [0x3a30] = "MEMDMATIME5", /* DMA Timing #5 */ 43 | [0x3a32] = "MEMDMATIME6", /* DMA Timing #6 */ 44 | [0x3a34] = "MEMDMATIME7", /* DMA Timing #7 */ 45 | [0x3a36] = "MEMDMATIME8", /* DMA Timing #8 */ 46 | [0x3a38] = "MEMDMASTRB", /* DMA Strobe Control */ 47 | 48 | [0x3a3a] = "MEMNANDCTRL", /* NAND FLASH Control */ 49 | [0x3a3c] = "MEMNANDTIME", /* NAND FLASH Timing */ 50 | [0x3a3e] = "MEMNANDECC0", /* NAND FLASH ECC0 */ 51 | [0x3a40] = "MEMNANDECC1", /* NAND FLASH ECC1 */ 52 | [0x3a42] = "MEMNANDECC2", /* NAND FLASH ECC2 */ 53 | [0x3a44] = "MEMNANDCNT", /* NAND FLASH Data Counter */ 54 | 55 | /* Bank A Memory (SDRAM) Control Register */ 56 | [0x3800] = "MEMCFGX", /* SDRAM Configuration */ 57 | [0x3802] = "MEMTIMEX0", /* SDRAM Timing #0 */ 58 | [0x3804] = "MEMTIMEX1", /* SDRAM Timing #1 */ 59 | [0x3806] = "MEMACTPWDX", /* Active Power Down Ctrl */ 60 | [0x3808] = "MEMREFX", /* Refresh Ctrl */ 61 | 62 | /* 63 | * Chapter 5 64 | * Clocks and Power Manager 65 | */ 66 | [0x0900] = "PWMODE", /* Power Mode */ 67 | [0x0902] = "CLKCHGST", /* Clock Change Status */ 68 | [0x0904] = "SYSCLKEN", /* System Clock Enable */ 69 | [0x0908] = "COMCLKEN", /* Communication Device Clk En */ 70 | [0x090a] = "VGCLKEN", /* Video & Graphic Device Clk En */ 71 | [0x090c] = "ASCLKEN", /* Audio & Storage Device Clk En */ 72 | [0x0910] = "FPLLSETV", /* FCLK PLL Setting Value Write */ 73 | [0x0912] = "FPLLVSET", /* FCLK PLL Value Setting */ 74 | [0x0914] = "UPLLSETV", /* UCLK PLL Setting Value Write */ 75 | [0x0916] = "UPLLVSET", /* UCLK PLL Value Setting */ 76 | [0x0918] = "APLLSETV", /* ACLK PLL Setting Value Write */ 77 | [0x091a] = "APLLVSET", /* ACLK PLL Value Setting */ 78 | [0x091c] = "SYSCSET", /* System CLK PLL Divide Value */ 79 | [0x091e] = "ESYSCSET", /* External System Clk Time Set */ 80 | [0x0920] = "UIRMCSET", /* USB/IRDA/MMC Clk Gen */ 81 | [0x0922] = "AUDICSET", /* Audio Ctrl Clk Gen */ 82 | [0x092e] = "SPDICSET", /* SPDIF Ctrl Clk Gen */ 83 | [0x0924] = "DISPCSET", /* Display Clk Gen */ 84 | [0x0926] = "IMAGCSET", /* Image Pixel Clk Gen */ 85 | [0x0928] = "URT0CSET", /* UART 0/1 Clk Gen */ 86 | [0x092a] = "UAR1CSET", /* UART 2/3 Clk Gen */ 87 | [0x092c] = "A940TMODE", /* ARM940T CPU Power Manage Mode */ 88 | 89 | /* 90 | * Interrupt 91 | */ 92 | [0x0800] = "SRCPEND", 93 | [0x0804] = "INTMOD", 94 | [0x0808] = "INTMASK", 95 | [0x080c] = "IPRIORITY", 96 | [0x0810] = "INTPEND", 97 | [0x0814] = "INTOFFSET", 98 | 99 | /* 100 | * DMA 101 | */ 102 | [0x0000] = "DMAINT", 103 | 104 | /* 105 | * UART 106 | */ 107 | 108 | [0x1200] = "ULCON0", 109 | [0x1202] = "UCON0", 110 | [0x1204] = "UFCON0", 111 | [0x1206] = "UMCON0", 112 | [0x1208] = "UTRSTAT0", 113 | [0x120a] = "UERRSTAT0", 114 | [0x120c] = "UFIFOSTAT0", 115 | [0x120e] = "UMODEMSTAT0", 116 | [0x1210] = "UTHB0", 117 | [0x1212] = "URHB0", 118 | [0x1214] = "UBRD0", 119 | [0x1216] = "UTIMEOUTREG0", 120 | 121 | [0x1220] = "ULCON1", 122 | [0x1222] = "UCON1", 123 | [0x1224] = "UFCON1", 124 | [0x1226] = "UMCON1", 125 | [0x1228] = "UTRSTAT1", 126 | [0x122a] = "UERRSTAT1", 127 | [0x122c] = "UFIFOSTAT1", 128 | [0x122e] = "UMODEMSTAT1", 129 | [0x1230] = "UTHB1", 130 | [0x1232] = "URHB1", 131 | [0x1234] = "UBRD1", 132 | [0x1236] = "UTIMEOUTREG1", 133 | 134 | [0x1240] = "ULCON2", 135 | [0x1242] = "UCON2", 136 | [0x1244] = "UFCON2", 137 | [0x1246] = "UMCON2", 138 | [0x1248] = "UTRSTAT2", 139 | [0x124a] = "UERRSTAT2", 140 | [0x124c] = "UFIFOSTAT2", 141 | [0x124e] = "UMODEMSTAT2", 142 | [0x1250] = "UTHB2", 143 | [0x1252] = "URHB2", 144 | [0x1254] = "UBRD2", 145 | [0x1256] = "UTIMEOUTREG2", 146 | 147 | [0x1260] = "ULCON3", 148 | [0x1262] = "UCON3", 149 | [0x1264] = "UFCON3", 150 | [0x1266] = "UMCON3", 151 | [0x1268] = "UTRSTAT3", 152 | [0x126a] = "UERRSTAT3", 153 | [0x126c] = "UFIFOSTAT3", 154 | [0x126e] = "UMODEMSTAT3", 155 | [0x1270] = "UTHB3", 156 | [0x1272] = "URHB3", 157 | [0x1274] = "UBRD3", 158 | [0x1276] = "UTIMEOUTREG3", 159 | 160 | [0x1280] = "UINTSTAT", 161 | [0x1282] = "UPORTCON", 162 | 163 | /* 164 | * Timer / Watch-dog 165 | */ 166 | [0x0a00] = "TCOUNT", 167 | [0x0a04] = "TMATCH0", 168 | [0x0a08] = "TMATCH1", 169 | [0x0a0c] = "TMATCH2", 170 | [0x0a10] = "TMATCH3", 171 | [0x0a14] = "TCONTROL", 172 | [0x0a16] = "TSTATUS", 173 | [0x0a18] = "TINTEN", 174 | 175 | /* 176 | * Real Time Clock (RTC) 177 | */ 178 | [0x0c00] = "RTCTSET", 179 | [0x0c04] = "RTCTCNT", 180 | [0x0c08] = "RTCSTCNT", 181 | [0x0c0a] = "TICKSET", 182 | [0x0c0c] = "ALARMT", 183 | [0x0c10] = "PWRMGR", 184 | [0x0c12] = "CLKMGR", 185 | [0x0c14] = "RSTCTRL", 186 | [0x0c16] = "RSTST", 187 | [0x0c18] = "BOOTCTRL", 188 | [0x0c1a] = "LOCKTIME", 189 | [0x0c1c] = "RSTTIME", 190 | [0x0c1e] = "EXTCTRL", 191 | [0x0c20] = "STOPTSET", 192 | [0x0c22] = "RTCCTRL", 193 | [0x0c24] = "RTSTRL", 194 | 195 | /* 196 | * I2C 197 | */ 198 | [0x0d00] = "IICCON", 199 | [0x0d02] = "IICSTAT", 200 | [0x0d04] = "IICADD", 201 | [0x0d06] = "IICDS", 202 | 203 | /* 204 | * AC97 205 | */ 206 | [0x0E00] = "AC_CTL", /* Control Register */ 207 | [0x0E02] = "AC_CONFIG", /* Config Register */ 208 | [0x0E04] = "AC_STA_EN", /* Status Enable Register */ 209 | [0x0E06] = "AC_GSR", /* Global Status Register */ 210 | [0x0E08] = "AC_ST_MCH", /* State Machine */ 211 | [0x0E0C] = "AC_ADDR", /* Codec Address Register */ 212 | [0x0E0E] = "AC_DATA", /* Codec Read Data Register */ 213 | [0x0E10] = "AC_CAR", /* Codec Access Register */ 214 | [0x0F00] = "AC_REG_BASE", /* AC97 Codec Register Base */ 215 | 216 | /* USB Device */ 217 | [0x1400] = "FUNC_ADDR_REG", 218 | [0x1402] = "PWR_REG", 219 | [0x1404] = "EP_INT_REG", 220 | [0x140C] = "USB_INT_REG", 221 | [0x140E] = "EP_INT_EN_REG", 222 | [0x1416] = "USB_INT_EN_REG", 223 | [0x1418] = "FRAME_NUM1_REG", 224 | [0x141A] = "FRAME_NUM2_REG", 225 | [0x141C] = "INDEX_REG", 226 | [0x1440] = "EP0_FIFO_REG", 227 | [0x1442] = "EP1_FIFO_REG", 228 | [0x1444] = "EP2_FIFO_REG", 229 | [0x1446] = "EP3_FIFO_REG", 230 | [0x1448] = "EP4_FIFO_REG", 231 | [0x1460] = "EP1_DMA_CON", 232 | [0x1464] = "EP1_DMA_FIFO", 233 | [0x1466] = "EP1_DMA_TTC_L", 234 | [0x1468] = "EP1_DMA_TTC_M", 235 | [0x146A] = "EP1_DMA_TTC_H", 236 | [0x146C] = "EP2_DMA_CON", 237 | [0x1470] = "EP2_DMA_FIFO", 238 | [0x1472] = "EP2_DMA_TTC_L", 239 | [0x1474] = "EP2_DMA_TTC_M", 240 | [0x1476] = "EP2_DMA_TTC_H", 241 | [0x1480] = "EP3_DMA_CON", 242 | [0x1484] = "EP3_DMA_FIFO", 243 | [0x1486] = "EP3_DMA_TTC_L", 244 | [0x1488] = "EP3_DMA_TTC_M", 245 | [0x148A] = "EP3_DMA_TTC_H", 246 | [0x148C] = "EP4_DMA_CON", 247 | [0x1490] = "EP4_DMA_FIFO", 248 | [0x1492] = "EP4_DMA_TTC_L", 249 | [0x1494] = "EP4_DMA_TTC_M", 250 | [0x1496] = "EP4_DMA_TTC_H", 251 | [0x1420] = "MAXP_REG", 252 | [0x1426] = "OUT_MAXP_REG", 253 | [0x1422] = "EP0_CSR", 254 | [0x1422] = "IN_CSR1_REG", 255 | [0x1424] = "IN_CSR2_REG", 256 | [0x1428] = "OUT_CSR1_REG", 257 | [0x142A] = "OUT_CSR2_REG", 258 | [0x142C] = "OUT_FIFO_CNT1_REG", 259 | [0x142E] = "OUT_FIFO_CNT2_REG", 260 | 261 | /* ADC/TP */ 262 | [0x4600] = "TPC_ADCCON", 263 | [0x4604] = "TPC_ADCDAT", 264 | [0x4640] = "TPC_CNTL", 265 | [0x4644] = "TPC_INTR", 266 | [0x4648] = "TPC_COMP_TP", 267 | [0x464c] = "TPC_COMP_U1", 268 | [0x4650] = "TPC_COMP_U2", 269 | [0x4654] = "TPC_CLK_CNTL", 270 | [0x4658] = "TPC_CH_SEL", 271 | [0x465c] = "TPC_TIME_PARM1", 272 | [0x4660] = "TPC_TIME_PARM2", 273 | [0x4664] = "TPC_TIME_PARM3", 274 | [0x4668] = "TPC_X_VALUE", 275 | [0x466c] = "TPC_Y_VALUE", 276 | [0x4670] = "TPC_AZ_VALUE", 277 | [0x4674] = "TPC_U1_VALUE", 278 | [0x4678] = "TPC_U2_VALUE", 279 | 280 | /* Dual CPU Interface: mmsp20_type.h */ 281 | [0x3B40] = "DINT920", 282 | [0x3B42] = "DINT940", 283 | [0x3B44] = "DPEND920", 284 | [0x3B46] = "DPEND940", 285 | [0x3B48] = "DCTRL940", 286 | 287 | /* FDC: mmsp20_type.h */ 288 | [0x1838] = "DFDC_CNTL", 289 | [0x183A] = "DFDC_FRAME_SIZE", 290 | [0x183C] = "DFDC_LUMA_OFFSET", 291 | [0x183E] = "DFDC_CB_OFFSET", 292 | [0x1840] = "DFDC_CR_OFFSET", 293 | [0x1842] = "DFDC_DST_BASE_L", 294 | [0x1844] = "DFDC_DST_BASE_H", 295 | [0x1846] = "DFDC_STATUS", 296 | [0x1848] = "DFDC_DERING", 297 | [0x184A] = "DFDC_OCC_CNTL", 298 | 299 | /* 300 | * Chapter 11 301 | * General Purpose I/O (GPIO) 302 | */ 303 | [0x1020] = "GPIOAALTFNLOW", 304 | [0x1022] = "GPIOBALTFNLOW", 305 | [0x1024] = "GPIOCALTFNLOW", 306 | [0x1026] = "GPIODALTFNLOW", 307 | [0x1028] = "GPIOEALTFNLOW", 308 | [0x102a] = "GPIOFALTFNLOW", 309 | [0x102c] = "GPIOGALTFNLOW", 310 | [0x102e] = "GPIOHALTFNLOW", 311 | [0x1030] = "GPIOIALTFNLOW", 312 | [0x1032] = "GPIOJALTFNLOW", 313 | [0x1034] = "GPIOKALTFNLOW", 314 | [0x1036] = "GPIOLALTFNLOW", 315 | [0x1038] = "GPIOMALTFNLOW", 316 | [0x103a] = "GPIONALTFNLOW", 317 | [0x103c] = "GPIOOALTFNLOW", 318 | 319 | [0x1040] = "GPIOAALTFNHI", 320 | [0x1042] = "GPIOBALTFNHI", 321 | [0x1044] = "GPIOCALTFNHI", 322 | [0x1046] = "GPIODALTFNHI", 323 | [0x1048] = "GPIOEALTFNHI", 324 | [0x104a] = "GPIOFALTFNHI", 325 | [0x104c] = "GPIOGALTFNHI", 326 | [0x104e] = "GPIOHALTFNHI", 327 | [0x1050] = "GPIOIALTFNHI", 328 | [0x1052] = "GPIOJALTFNHI", 329 | [0x1054] = "GPIOKALTFNHI", 330 | [0x1056] = "GPIOLALTFNHI", 331 | [0x1058] = "GPIOMALTFNHI", 332 | [0x105a] = "GPIONALTFNHI", 333 | [0x105c] = "GPIOOALTFNHI", 334 | 335 | [0x1180] = "GPIOAPINLVL", 336 | [0x1182] = "GPIOBPINLVL", 337 | [0x1184] = "GPIOCPINLVL", 338 | [0x1186] = "GPIODPINLVL", 339 | [0x1188] = "GPIOEPINLVL", 340 | [0x118a] = "GPIOFPINLVL", 341 | [0x118c] = "GPIOGPINLVL", 342 | [0x118e] = "GPIOHPINLVL", 343 | [0x1190] = "GPIOIPINLVL", 344 | [0x1192] = "GPIOJPINLVL", 345 | [0x1194] = "GPIOKPINLVL", 346 | [0x1196] = "GPIOLPINLVL", 347 | [0x1198] = "GPIOMPINLVL", 348 | [0x119a] = "GPIONPINLVL", 349 | [0x119c] = "GPIOOPINLVL", 350 | 351 | /* DPC */ 352 | [0x2800] = "DPC_CNTL", 353 | [0x2802] = "DPC_FPICNTL", 354 | [0x2804] = "DPC_FPIPOL1", 355 | [0x2806] = "DPC_FPIPOL2", 356 | [0x280a] = "DPC_FPIATV1", 357 | [0x280c] = "DPC_FPIATV2", 358 | [0x280e] = "DPC_FPIATV3", 359 | [0x2816] = "DPC_X_MAX", 360 | [0x2818] = "DPC_Y_MAX", 361 | [0x281a] = "DPC_HS_WIDTH", 362 | [0x281c] = "DPC_HS_STR", 363 | [0x281e] = "DPC_HS_END", 364 | [0x2820] = "DPC_V_SYNC", 365 | [0x2822] = "DPC_V_END", 366 | [0x2826] = "DPC_DE", 367 | [0x2828] = "DPC_PS", 368 | [0x282a] = "DPC_FG", 369 | [0x282c] = "DPC_LP", 370 | [0x2830] = "DPC_CLKV2", 371 | [0x2832] = "DPC_POL", 372 | [0x2834] = "DPC_CISSYNC", 373 | [0x283a] = "DPC_Y_BLANK", 374 | [0x283c] = "DPC_C_BLANK", 375 | [0x283e] = "DPC_YP_CSYNC", 376 | [0x2840] = "DPC_YN_CSYNC", 377 | [0x2842] = "DPC_CP_CSYNC", 378 | [0x2844] = "DPC_CN_CSYNC", 379 | [0x2846] = "DPC_INTR", 380 | [0x2848] = "DPC_CLKCNTL", 381 | 382 | /* MLC */ 383 | [0X2880] = "MLC_OVLAY_CNTR", 384 | [0X2882] = "MLC_YUV_EFECT", 385 | [0X2884] = "MLC_YUV_CNTL", 386 | [0X2886] = "MLC_YUVA_TP_HSC", 387 | [0X2888] = "MLC_YUVA_BT_HSC", 388 | [0X2898] = "MLC_VLA_ENDX", 389 | [0X288a] = "MLC_VLA_TP_VSCL", 390 | [0X288c] = "MLC_VLA_TP_VSCH", 391 | [0X2892] = "MLC_YUVA_TP_PXW", 392 | [0X2894] = "MLC_YUVA_BT_PXW", 393 | [0X28da] = "MLC_STL_CNTL", 394 | [0X28dc] = "MLC_STL_MIXMUX", 395 | [0X28de] = "MLC_STL_ALPHAL", 396 | [0X28e0] = "MLC_STL_ALPHAH", 397 | [0X28e2] = "MLC_STL1_STX", 398 | [0X28e4] = "MLC_STL1_ENDX", 399 | [0X28e6] = "MLC_STL1_STY", 400 | [0X28e8] = "MLC_STL1_ENDY", 401 | [0X28ea] = "MLC_STL2_STX", 402 | [0X28ec] = "MLC_STL2_ENDX", 403 | [0X28ee] = "MLC_STL2_STY", 404 | [0X28f0] = "MLC_STL2_ENDY", 405 | [0X28f2] = "MLC_STL3_STX", 406 | [0X28f4] = "MLC_STL3_ENDX", 407 | [0X28f6] = "MLC_STL3_STY", 408 | [0X28f8] = "MLC_STL3_ENDY", 409 | [0X28fa] = "MLC_STL4_STX", 410 | [0X28fc] = "MLC_STL4_ENDX", 411 | [0X28fe] = "MLC_STL4_STY", 412 | [0X2900] = "MLC_STL4_ENDY", 413 | [0X2902] = "MLC_STL_CKEY_GB", 414 | [0X2904] = "MLC_STL_CKEY_R", 415 | [0X2906] = "MLC_STL_HSC", 416 | [0X2908] = "MLC_STL_VSCL", 417 | [0X290a] = "MLC_STL_VSCH", 418 | [0X290c] = "MLC_STL_HW", 419 | [0X290e] = "MLC_STL_OADRL", 420 | [0X2910] = "MLC_STL_OADRH", 421 | [0X2912] = "MLC_STL_EADRL", 422 | [0X2914] = "MLC_STL_EADRH", 423 | [0X291e] = "MLC_HWC_CNTL", 424 | [0X2920] = "MLC_HWC_STX", 425 | [0X2922] = "MLC_HWC_STY", 426 | [0X2924] = "MLC_HWC_FGR", 427 | [0X2926] = "MLC_HWC_FB", 428 | [0X2928] = "MLC_HWC_BGR", 429 | [0X292a] = "MLC_HWC_BB", 430 | [0X292c] = "MLC_HWC_OADRL", 431 | [0X292e] = "MLC_HWC_OADRH", 432 | [0X2930] = "MLC_HWC_EADRL", 433 | [0X2932] = "MLC_HWC_EADRH", 434 | [0X2958] = "MLC_STL_PALLT_A", 435 | [0X295a] = "MLC_STL_PALLT_D", 436 | 437 | }; 438 | 439 | #endif /* _MMSP2_H */ 440 | -------------------------------------------------------------------------------- /loader/override.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011,2016 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "realfuncs.h" 22 | #include "syscalls.h" 23 | #include "llibc.h" 24 | #include "header.h" 25 | 26 | #if (DBG & 1) 27 | #define strace g_printf 28 | #else 29 | #define strace(...) 30 | #endif 31 | 32 | #define UNUSED __attribute__((unused)) 33 | 34 | static const struct dev_fd_t takeover_devs[] = { 35 | { "/dev/mem", FAKEDEV_MEM }, 36 | { "/dev/GPIO", FAKEDEV_GPIO }, 37 | { "/dev/fb0", FAKEDEV_FB0 }, 38 | { "/dev/fb/0", FAKEDEV_FB0 }, 39 | { "/dev/fb1", FAKEDEV_FB1 }, 40 | { "/dev/fb/1", FAKEDEV_FB1 }, 41 | { "/dev/mmuhack", FAKEDEV_MMUHACK }, 42 | { "/dev/tty", FAKEDEV_TTY }, 43 | { "/dev/tty0", FAKEDEV_TTY0 }, 44 | { "/dev/touchscreen/wm97xx", FAKEDEV_WM97XX }, 45 | { "/etc/pointercal", FAKEDEV_WM97XX_P }, 46 | { "/dev/input/mice", -ENODEV }, 47 | #ifdef PND 48 | { "/dev/input/event*", -ENODEV }, // hide for now, may cause dupe events 49 | #endif 50 | }; 51 | 52 | long w_open_raw(const char *pathname, int flags, mode_t mode) 53 | { 54 | long ret; 55 | int i; 56 | 57 | for (i = 0; i < ARRAY_SIZE(takeover_devs); i++) { 58 | const char *p, *oname; 59 | int len; 60 | 61 | oname = takeover_devs[i].name; 62 | p = strchr(oname, '*'); 63 | if (p != NULL) 64 | len = p - oname; 65 | else 66 | len = strlen(oname) + 1; 67 | 68 | if (strncmp(pathname, oname, len) == 0) { 69 | ret = takeover_devs[i].fd; 70 | break; 71 | } 72 | } 73 | 74 | if (i == ARRAY_SIZE(takeover_devs)) { 75 | const char *w_path = emu_wrap_path(pathname); 76 | ret = g_open_raw(w_path, flags, mode); 77 | emu_wrap_path_free(w_path, pathname); 78 | } 79 | 80 | if (ret >= 0) { 81 | for (i = 0; emu_interesting_fds[i].name != NULL; i++) { 82 | struct dev_fd_t *eifd = &emu_interesting_fds[i]; 83 | if (strcmp(pathname, eifd->name) == 0) { 84 | eifd->fd = ret; 85 | if (eifd->open_cb != NULL) 86 | eifd->open_cb(ret); 87 | break; 88 | } 89 | } 90 | } 91 | 92 | strace("open(%s) = %ld\n", pathname, ret); 93 | return ret; 94 | } 95 | 96 | static int w_open(const char *pathname, int flags, mode_t mode) 97 | { 98 | long ret = w_open_raw(pathname, flags, mode); 99 | return g_syscall_error(ret); 100 | } 101 | 102 | static long w_mmap_raw(void *addr, size_t length, int prot, int flags, 103 | int fd, off_t offset) 104 | { 105 | long ret; 106 | 107 | if (FAKEDEV_MEM <= fd && fd < FAKEDEV_FD_BOUNDARY) 108 | ret = emu_do_mmap(length, prot, flags, fd, offset); 109 | else 110 | ret = g_mmap2_raw(addr, length, prot, flags, fd, offset >> 12); 111 | 112 | strace("mmap(%p, %x, %x, %x, %d, %lx) = %lx\n", 113 | addr, length, prot, flags, fd, (long)offset, ret); 114 | return ret; 115 | } 116 | 117 | static long w_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) 118 | { 119 | long ret = w_mmap_raw(addr, length, prot, flags, fd, offset); 120 | return g_syscall_error(ret); 121 | } 122 | #define w_mmap2 w_mmap 123 | 124 | static long w_munmap_raw(void *addr, size_t length) 125 | { 126 | long ret; 127 | 128 | ret = emu_do_munmap(addr, length); 129 | if (ret == -EAGAIN) 130 | ret = g_munmap_raw(addr, length); 131 | 132 | strace("munmap(%p, %x) = %ld\n", addr, length, ret); 133 | return ret; 134 | } 135 | 136 | static int w_munmap(void *addr, size_t length) 137 | { 138 | long ret = w_munmap_raw(addr, length); 139 | return g_syscall_error(ret); 140 | } 141 | 142 | long w_read_raw(int fd, void *buf, size_t count) 143 | { 144 | long ret; 145 | 146 | if (FAKEDEV_MEM <= fd && fd < FAKEDEV_FD_BOUNDARY) 147 | ret = emu_do_read(fd, buf, count); 148 | else 149 | ret = g_read_raw(fd, buf, count); 150 | 151 | //strace("read(%d, %p, %zd) = %ld\n", fd, buf, count, ret); 152 | return ret; 153 | } 154 | 155 | static ssize_t w_read(int fd, void *buf, size_t count) 156 | { 157 | long ret = w_read_raw(fd, buf, count); 158 | return g_syscall_error(ret); 159 | } 160 | 161 | long w_ioctl_raw(int fd, int request, void *argp) 162 | { 163 | long ret; 164 | 165 | if ((FAKEDEV_MEM <= fd && fd < FAKEDEV_FD_BOUNDARY) || 166 | fd == emu_interesting_fds[IFD_SOUND].fd) 167 | ret = emu_do_ioctl(fd, request, argp); 168 | else 169 | ret = g_ioctl_raw(fd, request, argp); 170 | 171 | strace("ioctl(%d, %08x, %p) = %ld\n", fd, request, argp, ret); 172 | return ret; 173 | } 174 | 175 | static int w_ioctl(int fd, int request, void *argp) 176 | { 177 | long ret = w_ioctl_raw(fd, request, argp); 178 | return g_syscall_error(ret); 179 | } 180 | 181 | static int w_sigaction(int signum, const void *act, void *oldact) 182 | { 183 | strace("sigaction(%d, %p, %p) = %d\n", signum, act, oldact, 0); 184 | return 0; 185 | } 186 | 187 | /* dynamic only */ 188 | static UNUSED int w_tcgetattr(int fd, struct termios *termios_p) 189 | { 190 | int ret; 191 | if (fd != FAKEDEV_TTY0) 192 | ret = tcgetattr(fd, termios_p); 193 | else 194 | ret = 0; 195 | 196 | strace("tcgetattr(%d, %p) = %d\n", fd, termios_p, ret); 197 | return ret; 198 | } 199 | 200 | static UNUSED int w_tcsetattr(int fd, int optional_actions, 201 | const struct termios *termios_p) 202 | { 203 | int ret; 204 | if (fd != FAKEDEV_TTY0) 205 | ret = tcsetattr(fd, optional_actions, termios_p); 206 | else 207 | ret = 0; 208 | 209 | strace("tcsetattr(%d, %x, %p) = %d\n", fd, optional_actions, termios_p, ret); 210 | return ret; 211 | } 212 | 213 | static UNUSED FILE *w_fopen(const char *path, const char *mode) 214 | { 215 | FILE *ret = emu_do_fopen(path, mode); 216 | strace("fopen(%s, %s) = %p\n", path, mode, ret); 217 | return ret; 218 | } 219 | 220 | static UNUSED int w_system(const char *command) 221 | { 222 | int ret = emu_do_system(command); 223 | strace("system(%s) = %d\n", command, ret); 224 | return ret; 225 | } 226 | 227 | extern char **environ; 228 | 229 | static UNUSED int w_execl(const char *path, const char *arg, ...) 230 | { 231 | // don't allow exec (for now) 232 | strace("execl(%s, %s, ...) = ?\n", path, arg); 233 | exit(1); 234 | } 235 | 236 | static UNUSED int w_execlp(const char *file, const char *arg, ...) 237 | { 238 | strace("execlp(%s, %s, ...) = ?\n", file, arg); 239 | exit(1); 240 | } 241 | 242 | static UNUSED int w_execle(const char *path, const char *arg, ...) 243 | { 244 | strace("execle(%s, %s, ...) = ?\n", path, arg); 245 | exit(1); 246 | } 247 | 248 | static UNUSED int w_execv(const char *path, char *const argv[]) 249 | { 250 | strace("execv(%s, %p) = ?\n", path, argv); 251 | return emu_do_execve(path, argv, environ); 252 | } 253 | 254 | static UNUSED int w_execvp(const char *file, char *const argv[]) 255 | { 256 | strace("execvp(%s, %p) = ?\n", file, argv); 257 | return emu_do_execve(file, argv, environ); 258 | } 259 | 260 | long w_execve_raw(const char *filename, char * const argv[], 261 | char * const envp[]) 262 | { 263 | strace("execve(%s, %p, %p) = ?\n", filename, argv, envp); 264 | return emu_do_execve(filename, argv, envp); 265 | } 266 | 267 | static UNUSED int w_execve(const char *filename, char * const argv[], 268 | char * const envp[]) 269 | { 270 | long ret = w_execve_raw(filename, argv, envp); 271 | return g_syscall_error(ret); 272 | } 273 | 274 | static int w_chdir(const char *path) 275 | { 276 | long ret; 277 | 278 | if (path != NULL && strstr(path, "/usr/gp2x") != NULL) 279 | ret = 0; 280 | else 281 | ret = g_chdir_raw(path); 282 | 283 | strace("chdir(%s) = %ld\n", path, ret); 284 | return g_syscall_error(ret); 285 | } 286 | 287 | static ssize_t w_readlink(const char *path, char *buf, size_t bufsiz) 288 | { 289 | long ret; 290 | 291 | #ifndef DL 292 | if (path != NULL && strncmp(path, "/proc/", 6) == 0 293 | && strcmp(strrchr(path, '/'), "/exe") == 0) 294 | { 295 | ret = snprintf(buf, bufsiz, "%s", bin_path); 296 | if (ret > bufsiz) 297 | ret = bufsiz; 298 | } 299 | else 300 | #endif 301 | ret = g_readlink_raw(path, buf, bufsiz); 302 | 303 | strace("readlink(%s, %s, %zd) = %ld\n", path, buf, bufsiz, ret); 304 | return g_syscall_error(ret); 305 | } 306 | 307 | #undef open 308 | #undef fopen 309 | #undef mmap 310 | #undef munmap 311 | #undef read 312 | #undef ioctl 313 | #undef close 314 | #undef sigaction 315 | #undef tcgetattr 316 | #undef tcsetattr 317 | #undef system 318 | #undef execl 319 | #undef execlp 320 | #undef execle 321 | #undef execv 322 | #undef execvp 323 | #undef execve 324 | #undef chdir 325 | #undef readlink 326 | 327 | #ifdef DL 328 | 329 | #define MAKE_WRAP_SYM_N(sym) \ 330 | /* alias wrap symbols to real names */ \ 331 | typeof(sym) sym __attribute__((alias("w_" #sym))) 332 | 333 | #define MAKE_WRAP_SYM(sym) \ 334 | MAKE_WRAP_SYM_N(sym); \ 335 | /* wrapper to real functions, to be set up on load */ \ 336 | static typeof(sym) *p_real_##sym 337 | 338 | // note: update symver too 339 | MAKE_WRAP_SYM_N(open); 340 | MAKE_WRAP_SYM(fopen); 341 | MAKE_WRAP_SYM_N(mmap); 342 | MAKE_WRAP_SYM_N(munmap); 343 | MAKE_WRAP_SYM_N(read); 344 | MAKE_WRAP_SYM_N(ioctl); 345 | MAKE_WRAP_SYM(sigaction); 346 | MAKE_WRAP_SYM(tcgetattr); 347 | MAKE_WRAP_SYM(tcsetattr); 348 | MAKE_WRAP_SYM(system); 349 | MAKE_WRAP_SYM_N(execl); 350 | MAKE_WRAP_SYM_N(execlp); 351 | MAKE_WRAP_SYM_N(execle); 352 | MAKE_WRAP_SYM_N(execv); 353 | MAKE_WRAP_SYM_N(execvp); 354 | MAKE_WRAP_SYM_N(execve); 355 | MAKE_WRAP_SYM_N(chdir); 356 | MAKE_WRAP_SYM_N(readlink); 357 | typeof(mmap) mmap2 __attribute__((alias("w_mmap"))); 358 | 359 | #define REAL_FUNC_NP(name) \ 360 | { #name, (void **)&p_real_##name } 361 | 362 | static const struct { 363 | const char *name; 364 | void **func_ptr; 365 | } real_funcs_np[] = { 366 | //REAL_FUNC_NP(open), 367 | REAL_FUNC_NP(fopen), 368 | //REAL_FUNC_NP(mmap), 369 | //REAL_FUNC_NP(munmap), 370 | //REAL_FUNC_NP(read), 371 | //REAL_FUNC_NP(ioctl), 372 | REAL_FUNC_NP(sigaction), 373 | REAL_FUNC_NP(tcgetattr), 374 | REAL_FUNC_NP(tcsetattr), 375 | REAL_FUNC_NP(system), 376 | // exec* - skipped 377 | //REAL_FUNC_NP(execve), 378 | //REAL_FUNC_NP(chdir), 379 | }; 380 | 381 | //#define open p_real_open 382 | #define fopen p_real_fopen 383 | //#define mmap p_real_mmap 384 | //#define munmap p_real_munmap 385 | //#define read p_real_read 386 | //#define ioctl p_real_ioctl 387 | #define sigaction p_real_sigaction 388 | #define tcgetattr p_real_tcgetattr 389 | #define tcsetattr p_real_tcsetattr 390 | #define system p_real_system 391 | //#define execve p_real_execve 392 | //#define chdir p_real_chdir 393 | 394 | #undef MAKE_WRAP_SYM 395 | #undef REAL_FUNC_NP 396 | 397 | #endif 398 | 399 | // just call real funcs for static, pointer for dynamic 400 | int real_open(const char *pathname, int flags, ...) 401 | { 402 | va_list ap; 403 | mode_t mode; 404 | long ret; 405 | 406 | va_start(ap, flags); 407 | mode = va_arg(ap, mode_t); 408 | va_end(ap); 409 | ret = g_open_raw(pathname, flags, mode); 410 | return g_syscall_error(ret); 411 | } 412 | 413 | FILE *real_fopen(const char *path, const char *mode) 414 | { 415 | return fopen(path, mode); 416 | } 417 | 418 | void *real_mmap(void *addr, size_t length, int prot, int flags, 419 | int fd, off_t offset) 420 | { 421 | long ret = g_mmap2_raw(addr, length, prot, flags, fd, offset >> 12); 422 | return (void *)g_syscall_error(ret); 423 | } 424 | 425 | int real_munmap(void *addr, size_t length) 426 | { 427 | return g_syscall_error(g_munmap_raw(addr, length)); 428 | } 429 | 430 | int real_read(int fd, void *buf, size_t count) 431 | { 432 | long ret = g_read_raw(fd, buf, count); 433 | return g_syscall_error(ret); 434 | } 435 | 436 | int real_ioctl(int fd, int request, void *argp) 437 | { 438 | long ret = g_ioctl_raw(fd, request, argp); 439 | return g_syscall_error(ret); 440 | } 441 | 442 | int real_close(int fd) 443 | { 444 | long ret = g_close_raw(fd); 445 | return g_syscall_error(ret); 446 | } 447 | 448 | int real_sigaction(int signum, const sigaction_t *act, sigaction_t *oldact) 449 | { 450 | return sigaction(signum, act, oldact); 451 | } 452 | 453 | int real_tcgetattr(int fd, struct termios *termios_p) 454 | { 455 | return tcgetattr(fd, termios_p); 456 | } 457 | 458 | int real_tcsetattr(int fd, int optional_actions, 459 | const struct termios *termios_p) 460 | { 461 | return tcsetattr(fd, optional_actions, termios_p); 462 | } 463 | 464 | int real_system(const char *command) 465 | { 466 | return system(command); 467 | } 468 | 469 | // real_exec* is missing intentionally - we don't need them 470 | 471 | int real_execve(const char *filename, char *const argv[], 472 | char *const envp[]) 473 | { 474 | long ret = g_execve_raw(filename, argv, envp); 475 | return g_syscall_error(ret); 476 | } 477 | 478 | int real_chdir(const char *path) 479 | { 480 | long ret = g_chdir_raw(path); 481 | return g_syscall_error(ret); 482 | } 483 | 484 | ssize_t real_readlink(const char *path, char *buf, size_t bufsiz) 485 | { 486 | long ret = g_readlink_raw(path, buf, bufsiz); 487 | return g_syscall_error(ret); 488 | } 489 | 490 | void real_sleep(unsigned int seconds) 491 | { 492 | struct timespec ts = { seconds, 0 }; 493 | g_nanosleep_raw(&ts, NULL); 494 | } 495 | 496 | void real_usleep(unsigned int usec) 497 | { 498 | struct timespec ts = { usec / 1000000, usec % 1000000 }; 499 | g_nanosleep_raw(&ts, NULL); 500 | } 501 | 502 | void real_exit(int status) 503 | { 504 | g_exit_group_raw(status); 505 | } 506 | 507 | // vim:shiftwidth=2:expandtab 508 | -------------------------------------------------------------------------------- /loader/patches.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011,2016 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | #include 8 | 9 | #include "header.h" 10 | #include "syscalls.h" 11 | 12 | #include "override.c" 13 | 14 | // note: first mask int must be always full for the search algo 15 | static const unsigned int sig_mask_all[] = { 16 | 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 17 | 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff 18 | }; 19 | 20 | static const unsigned int sig_open[] = { 21 | 0xe59cc000, // ldr ip, [ip] 22 | 0xe33c0000, // teq ip, #0 23 | 0x1a000003, // bne 0x1c 24 | 0xef900005, // svc 0x900005 25 | }; 26 | #define sig_mask_open sig_mask_all 27 | 28 | static const unsigned int sig_open_a1[] = { 29 | 0xef900005, // svc 0x900005 30 | 0xe1a0f00e, // mov pc, lr 31 | }; 32 | #define sig_mask_open_a1 sig_mask_all 33 | 34 | static const unsigned int sig_hw_open[] = { 35 | 0xef900005, // svc 0x900005 36 | 0xe3700a01, // cmn r0, #0x1000 37 | 0xe1a04000, // mov r4, r0 38 | }; 39 | #define sig_mask_hw_open sig_mask_all 40 | 41 | static const unsigned int sig_mmap[] = { 42 | 0xe92d000f, // push {r0, r1, r2, r3} 43 | 0xe1a0000d, // mov r0, sp 44 | 0xef90005a, // svc 0x90005a 45 | 0xe28dd010, // add sp, sp, #16 46 | }; 47 | #define sig_mask_mmap sig_mask_all 48 | 49 | static const unsigned int sig_munmap[] = { 50 | 0xef90005b, // svc 0x90005b 51 | 0xe3700a01, // cmn r0, #0x1000 52 | 0x312fff1e, // bxcc lr 53 | }; 54 | #define sig_mask_munmap sig_mask_all 55 | 56 | static const unsigned int sig_mmap2[] = { 57 | 0xe52d5004, // push {r5} 58 | 0xe59d5008, // ldr r5, [sp, #8] 59 | 0xe52d4004, // push {r4} 60 | 0xe59d4008, // ldr r4, [sp, #8] 61 | 0xe1b0ca05, // lsls ip, r5, #20 62 | 0x1a000006, // bne 0x34 63 | 0xe1a05625, // lsr r5, r5, #12 64 | 0xef9000c0, // svc 0x9000c0 65 | }; 66 | #define sig_mask_mmap2 sig_mask_all 67 | 68 | static const unsigned int sig_read[] = { 69 | 0xe59fc080, // ldr ip, [pc, #128] 70 | 0xe59cc000, // ldr ip, [ip] 71 | 0xe33c0000, // teq ip, #0 72 | 0x1a000003, // bne 0x20 73 | 0xef900003, // svc 0x900003 74 | }; 75 | #define sig_mask_read sig_mask_all 76 | 77 | static const unsigned int sig_read_a1[] = { 78 | 0xef900003, // svc 0x900003 79 | 0xe3700a01, // cmn r0, #0x1000 80 | 0x312fff1e, // bxcc lr 81 | }; 82 | #define sig_mask_read_a1 sig_mask_all 83 | 84 | static const unsigned int sig_hw_read[] = { 85 | 0xef900003, // svc 0x900003 86 | 0xe3700a01, // cmn r0, #0x1000 87 | 0xe1a04000, // mov r4, r0 88 | }; 89 | #define sig_mask_hw_read sig_mask_all 90 | 91 | static const unsigned int sig_ioctl[] = { 92 | 0xef900036, // svc 0x900036 93 | 0xe3700a01, // cmn r0, #0x1000 94 | 0x312fff1e, // bxcc lr 95 | }; 96 | #define sig_mask_ioctl sig_mask_all 97 | 98 | static const unsigned int sig_hw_ioctl[] = { 99 | 0xef900036, // svc 0x900036 100 | 0xe3700a01, // cmn r0, #0x1000 101 | 0xe1a04000, // mov r4, r0 102 | }; 103 | #define sig_mask_hw_ioctl sig_mask_all 104 | 105 | static const unsigned int sig_sigaction[] = { 106 | 0xe59f300c, // ldr r3, [pc, #12] 107 | 0xe3530000, // cmp r3, #0 108 | 0x0a000000, // beq 0f 109 | 0xea000000, // b * 110 | 0xea000000, // 0: b * 111 | }; 112 | static const unsigned int sig_mask_sigaction[] = { 113 | 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000 114 | }; 115 | 116 | static const unsigned int sig_execve[] = { 117 | 0xef90000b, // svc 0x90000b 118 | 0xe1a04000, // mov r4, r0 119 | 0xe3700a01, // cmn r0, #4096 120 | }; 121 | #define sig_mask_execve sig_mask_all 122 | 123 | static const unsigned int sig_hw_execve[] = { 124 | 0xef90000b, // svc 0x90000b 125 | 0xe3700a01, // cmn r0, #4096 126 | 0xe1a04000, // mov r4, r0 127 | }; 128 | #define sig_mask_hw_execve sig_mask_all 129 | 130 | static const unsigned int sig_chdir[] = { 131 | 0xef90000c, // svc 0x90000c 132 | 0xe3700a01, // cmn r0, #4096 133 | 0x312fff1e, // bxcc lr 134 | 0xea0004bb, // b * 135 | }; 136 | static const unsigned int sig_mask_chdir[] = { 137 | 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000 138 | }; 139 | 140 | static const unsigned int sig_readlink[] = { 141 | 0xef900055, // svc 0x900055 142 | 0xe3700a01, // cmn r0, #0x1000 143 | 0x312fff1e, // bxcc lr 144 | }; 145 | #define sig_mask_readlink sig_mask_all 146 | 147 | /* special */ 148 | static const unsigned int sig_cache1[] = { 149 | 0xee073f5e, // mcr 15, 0, r3, cr7, cr14, 2 150 | }; 151 | #define sig_mask_cache1 sig_mask_all 152 | 153 | static const unsigned int sig_cache2[] = { 154 | 0xee070f17, // mcr 15, 0, r0, cr7, cr7, 0 155 | }; 156 | #define sig_mask_cache2 sig_mask_all 157 | 158 | /* additional wrappers for harder case of syscalls within the code stream */ 159 | #ifdef PND /* fix PC, not needed on ARM9 */ 160 | # define SVC_CMN_R0_MOV_R4_PC_ADJ() \ 161 | " ldr r12, [sp, #5*4]\n" \ 162 | " add r12, r12, #4\n" \ 163 | " str r12, [sp, #5*4]\n" 164 | #else 165 | # define SVC_CMN_R0_MOV_R4_PC_ADJ() 166 | #endif 167 | 168 | #define SVC_CMN_R0_MOV_R4_WRAPPER(name, target) \ 169 | extern int name(); \ 170 | asm( \ 171 | #name ":\n" \ 172 | " stmfd sp!, {r1-r3,r12,lr}\n" \ 173 | SVC_CMN_R0_MOV_R4_PC_ADJ() \ 174 | " bl " #target "\n" \ 175 | " cmn r0, #0x1000\n" \ 176 | " mov r4, r0\n" \ 177 | " ldmfd sp!, {r1-r3,r12,lr,pc}\n" \ 178 | ); 179 | 180 | SVC_CMN_R0_MOV_R4_WRAPPER(hw_open, w_open_raw) 181 | SVC_CMN_R0_MOV_R4_WRAPPER(hw_read, w_read_raw) 182 | SVC_CMN_R0_MOV_R4_WRAPPER(hw_ioctl, w_ioctl_raw) 183 | SVC_CMN_R0_MOV_R4_WRAPPER(hw_execve, w_execve_raw) 184 | 185 | #define PATCH_(p, f, t) { sig_##p, sig_mask_##p, ARRAY_SIZE(sig_##p), t, f, #p } 186 | #define PATCH(f) PATCH_(f, w_##f, 0) 187 | 188 | static const struct { 189 | const unsigned int *sig; 190 | const unsigned int *sig_mask; 191 | size_t sig_cnt; 192 | unsigned int type; 193 | void *func; 194 | const char *name; 195 | } patches[] = { 196 | PATCH (open), 197 | PATCH_(open_a1, w_open, 0), 198 | PATCH_(hw_open, hw_open, 1), 199 | PATCH (mmap), 200 | PATCH (mmap2), // mmap2 syscall 201 | PATCH (munmap), 202 | PATCH (read), 203 | PATCH_(read_a1, w_read, 0), 204 | PATCH_(hw_read, hw_read, 1), 205 | PATCH (ioctl), 206 | PATCH_(hw_ioctl, hw_ioctl, 1), 207 | PATCH (sigaction), 208 | PATCH_(hw_execve, hw_execve, 1), 209 | PATCH (chdir), 210 | PATCH (readlink), 211 | PATCH_(cache1, NULL, 2), 212 | PATCH_(cache2, NULL, 2), 213 | }; 214 | 215 | void do_patches(void *ptr, unsigned int size) 216 | { 217 | unsigned int *seg = (void *)(((long)ptr + 3) & ~3); 218 | unsigned int *seg_end = seg + size / 4; 219 | int i, s; 220 | 221 | for (; seg < seg_end; seg++) { 222 | for (i = 0; i < ARRAY_SIZE(patches); i++) { 223 | const unsigned int *sig = patches[i].sig; 224 | const unsigned int *sig_mask; 225 | 226 | if (*seg != sig[0]) 227 | continue; 228 | 229 | sig_mask = patches[i].sig_mask; 230 | for (s = 1; s < patches[i].sig_cnt; s++) 231 | if ((seg[s] ^ sig[s]) & sig_mask[s]) 232 | break; 233 | 234 | if (s == patches[i].sig_cnt) { 235 | switch (patches[i].type) { 236 | case 0: 237 | seg[0] = 0xe51ff004; // ldr pc, [pc, #-4] 238 | seg[1] = (unsigned int)patches[i].func; 239 | break; 240 | case 1: 241 | seg[0] = 0xe92d8000; // stmfd sp!, {pc} 242 | seg[1] = 0xe51ff004; // ldr pc, [pc, #-4] 243 | seg[2] = (unsigned int)patches[i].func; 244 | break; 245 | case 2: 246 | if (seg < (unsigned int *)ptr + 1 || (seg[-1] >> 28) != 0x0e) 247 | // might be data 248 | continue; 249 | seg[0] = 0xe1a00000; // nop 250 | break; 251 | default: 252 | err("bad patch type: %u\n", patches[i].type); 253 | abort(); 254 | } 255 | dbg(" patch #%2i @ %08x type %d %s\n", 256 | i, (int)seg, patches[i].type, patches[i].name); 257 | seg += patches[i].sig_cnt - 1; 258 | break; 259 | } 260 | } 261 | } 262 | 263 | sys_cacheflush(ptr, (char *)ptr + size); 264 | } 265 | 266 | // vim:shiftwidth=2:expandtab 267 | -------------------------------------------------------------------------------- /loader/realfuncs.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "llibc.h" 13 | 14 | #define printf(fmt, ...) \ 15 | g_fprintf(1, fmt, ##__VA_ARGS__) 16 | 17 | int real_open(const char *pathname, int flags, ...); 18 | FILE *real_fopen(const char *path, const char *mode); 19 | void *real_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 20 | int real_munmap(void *addr, size_t length); 21 | int real_read(int fd, void *buf, size_t count); 22 | int real_ioctl(int fd, int request, void *argp); 23 | int real_close(int fd); 24 | int real_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 25 | typedef struct sigaction sigaction_t; 26 | int real_tcgetattr(int fd, struct termios *termios_p); 27 | int real_tcsetattr(int fd, int optional_actions, const struct termios *termios_p); 28 | int real_system(const char *command); 29 | // exec* - skipped 30 | int real_execve(const char *filename, char *const argv[], char *const envp[]); 31 | int real_chdir(const char *path); 32 | void real_sleep(unsigned int seconds); 33 | void real_usleep(unsigned int usec); 34 | void __attribute__((noreturn)) 35 | real_exit(int status); 36 | 37 | #define open real_open 38 | #define fopen real_fopen 39 | #define mmap real_mmap 40 | #define munmap real_munmap 41 | #define read real_read 42 | #define ioctl real_ioctl 43 | #define close real_close 44 | #define sigaction real_sigaction 45 | #define tcgetattr real_tcgetattr 46 | #define tcsetattr real_tcsetattr 47 | #define system real_system 48 | #define execl real_execl 49 | #define execlp real_execlp 50 | #define execle real_execle 51 | #define execv real_execv 52 | #define execvp real_execvp 53 | #define execve real_execve 54 | #define chdir real_chdir 55 | #define sleep real_sleep 56 | #define usleep real_usleep 57 | #define exit real_exit 58 | 59 | -------------------------------------------------------------------------------- /loader/script_arm.lds: -------------------------------------------------------------------------------- 1 | /* Script for -z combreloc: combine and sort reloc sections */ 2 | OUTPUT_FORMAT("elf32-littlearm") 3 | OUTPUT_ARCH(arm) 4 | ENTRY(_start) 5 | SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); 6 | SECTIONS 7 | { 8 | mybase = 0x70000000; /* 0x00008000 */ 9 | /* Read-only sections, merged into text segment: */ 10 | PROVIDE (__executable_start = mybase); 11 | . = mybase + SIZEOF_HEADERS; 12 | .interp : { *(.interp) } 13 | .hash : { *(.hash) } 14 | .dynsym : { *(.dynsym) } 15 | .dynstr : { *(.dynstr) } 16 | .gnu.version : { *(.gnu.version) } 17 | .gnu.version_d : { *(.gnu.version_d) } 18 | .gnu.version_r : { *(.gnu.version_r) } 19 | .rel.dyn : 20 | { 21 | *(.rel.init) 22 | *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) 23 | *(.rel.fini) 24 | *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) 25 | *(.rel.data.rel.ro*) 26 | *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) 27 | *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) 28 | *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) 29 | *(.rel.ctors) 30 | *(.rel.dtors) 31 | *(.rel.got) 32 | *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) 33 | } 34 | .rela.dyn : 35 | { 36 | *(.rela.init) 37 | *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) 38 | *(.rela.fini) 39 | *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) 40 | *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) 41 | *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) 42 | *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) 43 | *(.rela.ctors) 44 | *(.rela.dtors) 45 | *(.rela.got) 46 | *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) 47 | } 48 | .rel.plt : { *(.rel.plt) } 49 | .rela.plt : { *(.rela.plt) } 50 | .init : 51 | { 52 | KEEP (*(.init)) 53 | } =0 54 | .plt : { *(.plt) } 55 | .text : 56 | { 57 | *(.text .stub .text.* .gnu.linkonce.t.*) 58 | KEEP (*(.text.*personality*)) 59 | /* .gnu.warning sections are handled specially by elf32.em. */ 60 | *(.gnu.warning) 61 | *(.glue_7t) *(.glue_7) 62 | } =0 63 | .fini : 64 | { 65 | KEEP (*(.fini)) 66 | } =0 67 | PROVIDE (__etext = .); 68 | PROVIDE (_etext = .); 69 | PROVIDE (etext = .); 70 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } 71 | .rodata1 : { *(.rodata1) } 72 | .eh_frame_hdr : { *(.eh_frame_hdr) } 73 | .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } 74 | .gcc_except_table : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) } 75 | /* Adjust the address for the data segment. We want to adjust up to 76 | the same address within the page on the next page up. */ 77 | . = ALIGN (0x8000) - ((0x8000 - .) & (0x8000 - 1)); . = DATA_SEGMENT_ALIGN (0x8000, 0x1000); 78 | /* Exception handling */ 79 | .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) } 80 | .gcc_except_table : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) } 81 | /* Thread Local Storage sections */ 82 | .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } 83 | .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } 84 | /* Ensure the __preinit_array_start label is properly aligned. We 85 | could instead move the label definition inside the section, but 86 | the linker would then create the section even if it turns out to 87 | be empty, which isn't pretty. */ 88 | . = ALIGN(32 / 8); 89 | PROVIDE (__preinit_array_start = .); 90 | .preinit_array : { KEEP (*(.preinit_array)) } 91 | PROVIDE (__preinit_array_end = .); 92 | PROVIDE (__init_array_start = .); 93 | .init_array : { KEEP (*(.init_array)) } 94 | PROVIDE (__init_array_end = .); 95 | PROVIDE (__fini_array_start = .); 96 | .fini_array : { KEEP (*(.fini_array)) } 97 | PROVIDE (__fini_array_end = .); 98 | .ctors : 99 | { 100 | /* gcc uses crtbegin.o to find the start of 101 | the constructors, so we make sure it is 102 | first. Because this is a wildcard, it 103 | doesn't matter if the user does not 104 | actually link against crtbegin.o; the 105 | linker won't look for a file to match a 106 | wildcard. The wildcard also means that it 107 | doesn't matter which directory crtbegin.o 108 | is in. */ 109 | KEEP (*crtbegin*.o(.ctors)) 110 | /* We don't want to include the .ctor section from 111 | from the crtend.o file until after the sorted ctors. 112 | The .ctor section from the crtend file contains the 113 | end of ctors marker and it must be last */ 114 | KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors)) 115 | KEEP (*(SORT(.ctors.*))) 116 | KEEP (*(.ctors)) 117 | } 118 | .dtors : 119 | { 120 | KEEP (*crtbegin*.o(.dtors)) 121 | KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors)) 122 | KEEP (*(SORT(.dtors.*))) 123 | KEEP (*(.dtors)) 124 | } 125 | .jcr : { KEEP (*(.jcr)) } 126 | .data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) } 127 | .dynamic : { *(.dynamic) } 128 | . = DATA_SEGMENT_RELRO_END (0, .); 129 | .got : { *(.got.plt) *(.got) } 130 | .data : 131 | { 132 | __data_start = . ; 133 | *(.data .data.* .gnu.linkonce.d.*) 134 | KEEP (*(.gnu.linkonce.d.*personality*)) 135 | SORT(CONSTRUCTORS) 136 | } 137 | .data1 : { *(.data1) } 138 | _edata = .; 139 | PROVIDE (edata = .); 140 | __bss_start = .; 141 | __bss_start__ = .; 142 | .bss : 143 | { 144 | *(.dynbss) 145 | *(.bss .bss.* .gnu.linkonce.b.*) 146 | *(COMMON) 147 | /* Align here to ensure that the .bss section occupies space up to 148 | _end. Align after .bss to ensure correct alignment even if the 149 | .bss section disappears because there are no input sections. */ 150 | . = ALIGN(32 / 8); 151 | } 152 | . = ALIGN(32 / 8); 153 | _end = .; 154 | _bss_end__ = . ; __bss_end__ = . ; __end__ = . ; 155 | PROVIDE (end = .); 156 | . = DATA_SEGMENT_END (.); 157 | /* Stabs debugging sections. */ 158 | .stab 0 : { *(.stab) } 159 | .stabstr 0 : { *(.stabstr) } 160 | .stab.excl 0 : { *(.stab.excl) } 161 | .stab.exclstr 0 : { *(.stab.exclstr) } 162 | .stab.index 0 : { *(.stab.index) } 163 | .stab.indexstr 0 : { *(.stab.indexstr) } 164 | .comment 0 : { *(.comment) } 165 | /* DWARF debug sections. 166 | Symbols in the DWARF debugging sections are relative to the beginning 167 | of the section so we begin them at 0. */ 168 | /* DWARF 1 */ 169 | .debug 0 : { *(.debug) } 170 | .line 0 : { *(.line) } 171 | /* GNU DWARF 1 extensions */ 172 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 173 | .debug_sfnames 0 : { *(.debug_sfnames) } 174 | /* DWARF 1.1 and DWARF 2 */ 175 | .debug_aranges 0 : { *(.debug_aranges) } 176 | .debug_pubnames 0 : { *(.debug_pubnames) } 177 | /* DWARF 2 */ 178 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } 179 | .debug_abbrev 0 : { *(.debug_abbrev) } 180 | .debug_line 0 : { *(.debug_line) } 181 | .debug_frame 0 : { *(.debug_frame) } 182 | .debug_str 0 : { *(.debug_str) } 183 | .debug_loc 0 : { *(.debug_loc) } 184 | .debug_macinfo 0 : { *(.debug_macinfo) } 185 | /* SGI/MIPS DWARF 2 extensions */ 186 | .debug_weaknames 0 : { *(.debug_weaknames) } 187 | .debug_funcnames 0 : { *(.debug_funcnames) } 188 | .debug_typenames 0 : { *(.debug_typenames) } 189 | .debug_varnames 0 : { *(.debug_varnames) } 190 | .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } 191 | /DISCARD/ : { *(.note.GNU-stack) } 192 | } 193 | 194 | 195 | -------------------------------------------------------------------------------- /loader/script_ia32.lds: -------------------------------------------------------------------------------- 1 | /* Script for -z combreloc: combine and sort reloc sections */ 2 | OUTPUT_FORMAT("elf32-i386") 3 | OUTPUT_ARCH(i386) 4 | ENTRY(_start) 5 | SEARCH_DIR("/usr/local/lib32"); SEARCH_DIR("/lib32"); SEARCH_DIR("/usr/lib32"); 6 | SECTIONS 7 | { 8 | mybase = 0x70000000; /* 0x08048000 */ 9 | /* Read-only sections, merged into text segment: */ 10 | PROVIDE (__executable_start = SEGMENT_START("text-segment", mybase)); 11 | . = SEGMENT_START("text-segment", mybase) + SIZEOF_HEADERS; 12 | .interp : { *(.interp) } 13 | .note.gnu.build-id : { *(.note.gnu.build-id) } 14 | .hash : { *(.hash) } 15 | .gnu.hash : { *(.gnu.hash) } 16 | .dynsym : { *(.dynsym) } 17 | .dynstr : { *(.dynstr) } 18 | .gnu.version : { *(.gnu.version) } 19 | .gnu.version_d : { *(.gnu.version_d) } 20 | .gnu.version_r : { *(.gnu.version_r) } 21 | .rel.dyn : 22 | { 23 | *(.rel.init) 24 | *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) 25 | *(.rel.fini) 26 | *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) 27 | *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) 28 | *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) 29 | *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) 30 | *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) 31 | *(.rel.ctors) 32 | *(.rel.dtors) 33 | *(.rel.got) 34 | *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) 35 | *(.rel.ifunc) 36 | } 37 | .rel.plt : 38 | { 39 | *(.rel.plt) 40 | PROVIDE_HIDDEN (__rel_iplt_start = .); 41 | *(.rel.iplt) 42 | PROVIDE_HIDDEN (__rel_iplt_end = .); 43 | } 44 | .init : 45 | { 46 | KEEP (*(.init)) 47 | } =0x90909090 48 | .plt : { *(.plt) *(.iplt) } 49 | .text : 50 | { 51 | *(.text.unlikely .text.*_unlikely) 52 | *(.text .stub .text.* .gnu.linkonce.t.*) 53 | /* .gnu.warning sections are handled specially by elf32.em. */ 54 | *(.gnu.warning) 55 | } =0x90909090 56 | .fini : 57 | { 58 | KEEP (*(.fini)) 59 | } =0x90909090 60 | PROVIDE (__etext = .); 61 | PROVIDE (_etext = .); 62 | PROVIDE (etext = .); 63 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } 64 | .rodata1 : { *(.rodata1) } 65 | .eh_frame_hdr : { *(.eh_frame_hdr) } 66 | .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } 67 | .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } 68 | /* Adjust the address for the data segment. We want to adjust up to 69 | the same address within the page on the next page up. */ 70 | . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); 71 | /* Exception handling */ 72 | .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) } 73 | .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } 74 | /* Thread Local Storage sections */ 75 | .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } 76 | .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } 77 | .preinit_array : 78 | { 79 | PROVIDE_HIDDEN (__preinit_array_start = .); 80 | KEEP (*(.preinit_array)) 81 | PROVIDE_HIDDEN (__preinit_array_end = .); 82 | } 83 | .init_array : 84 | { 85 | PROVIDE_HIDDEN (__init_array_start = .); 86 | KEEP (*(SORT(.init_array.*))) 87 | KEEP (*(.init_array)) 88 | PROVIDE_HIDDEN (__init_array_end = .); 89 | } 90 | .fini_array : 91 | { 92 | PROVIDE_HIDDEN (__fini_array_start = .); 93 | KEEP (*(.fini_array)) 94 | KEEP (*(SORT(.fini_array.*))) 95 | PROVIDE_HIDDEN (__fini_array_end = .); 96 | } 97 | .ctors : 98 | { 99 | /* gcc uses crtbegin.o to find the start of 100 | the constructors, so we make sure it is 101 | first. Because this is a wildcard, it 102 | doesn't matter if the user does not 103 | actually link against crtbegin.o; the 104 | linker won't look for a file to match a 105 | wildcard. The wildcard also means that it 106 | doesn't matter which directory crtbegin.o 107 | is in. */ 108 | KEEP (*crtbegin.o(.ctors)) 109 | KEEP (*crtbegin?.o(.ctors)) 110 | /* We don't want to include the .ctor section from 111 | the crtend.o file until after the sorted ctors. 112 | The .ctor section from the crtend file contains the 113 | end of ctors marker and it must be last */ 114 | KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) 115 | KEEP (*(SORT(.ctors.*))) 116 | KEEP (*(.ctors)) 117 | } 118 | .dtors : 119 | { 120 | KEEP (*crtbegin.o(.dtors)) 121 | KEEP (*crtbegin?.o(.dtors)) 122 | KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) 123 | KEEP (*(SORT(.dtors.*))) 124 | KEEP (*(.dtors)) 125 | } 126 | .jcr : { KEEP (*(.jcr)) } 127 | .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) } 128 | .dynamic : { *(.dynamic) } 129 | .got : { *(.got) *(.igot) } 130 | . = DATA_SEGMENT_RELRO_END (12, .); 131 | .got.plt : { *(.got.plt) *(.igot.plt) } 132 | .data : 133 | { 134 | *(.data .data.* .gnu.linkonce.d.*) 135 | SORT(CONSTRUCTORS) 136 | } 137 | .data1 : { *(.data1) } 138 | _edata = .; PROVIDE (edata = .); 139 | __bss_start = .; 140 | .bss : 141 | { 142 | *(.dynbss) 143 | *(.bss .bss.* .gnu.linkonce.b.*) 144 | *(COMMON) 145 | /* Align here to ensure that the .bss section occupies space up to 146 | _end. Align after .bss to ensure correct alignment even if the 147 | .bss section disappears because there are no input sections. 148 | FIXME: Why do we need it? When there is no .bss section, we don't 149 | pad the .data section. */ 150 | . = ALIGN(. != 0 ? 32 / 8 : 1); 151 | } 152 | . = ALIGN(32 / 8); 153 | . = ALIGN(32 / 8); 154 | _end = .; PROVIDE (end = .); 155 | . = DATA_SEGMENT_END (.); 156 | /* Stabs debugging sections. */ 157 | .stab 0 : { *(.stab) } 158 | .stabstr 0 : { *(.stabstr) } 159 | .stab.excl 0 : { *(.stab.excl) } 160 | .stab.exclstr 0 : { *(.stab.exclstr) } 161 | .stab.index 0 : { *(.stab.index) } 162 | .stab.indexstr 0 : { *(.stab.indexstr) } 163 | .comment 0 : { *(.comment) } 164 | /* DWARF debug sections. 165 | Symbols in the DWARF debugging sections are relative to the beginning 166 | of the section so we begin them at 0. */ 167 | /* DWARF 1 */ 168 | .debug 0 : { *(.debug) } 169 | .line 0 : { *(.line) } 170 | /* GNU DWARF 1 extensions */ 171 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 172 | .debug_sfnames 0 : { *(.debug_sfnames) } 173 | /* DWARF 1.1 and DWARF 2 */ 174 | .debug_aranges 0 : { *(.debug_aranges) } 175 | .debug_pubnames 0 : { *(.debug_pubnames) } 176 | /* DWARF 2 */ 177 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } 178 | .debug_abbrev 0 : { *(.debug_abbrev) } 179 | .debug_line 0 : { *(.debug_line) } 180 | .debug_frame 0 : { *(.debug_frame) } 181 | .debug_str 0 : { *(.debug_str) } 182 | .debug_loc 0 : { *(.debug_loc) } 183 | .debug_macinfo 0 : { *(.debug_macinfo) } 184 | /* SGI/MIPS DWARF 2 extensions */ 185 | .debug_weaknames 0 : { *(.debug_weaknames) } 186 | .debug_funcnames 0 : { *(.debug_funcnames) } 187 | .debug_typenames 0 : { *(.debug_typenames) } 188 | .debug_varnames 0 : { *(.debug_varnames) } 189 | /* DWARF 3 */ 190 | .debug_pubtypes 0 : { *(.debug_pubtypes) } 191 | .debug_ranges 0 : { *(.debug_ranges) } 192 | .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } 193 | /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } 194 | } 195 | 196 | -------------------------------------------------------------------------------- /loader/syscalls.S: -------------------------------------------------------------------------------- 1 | @ vim:filetype=armasm 2 | #include 3 | 4 | @ support ancient toolchains (gcc 2.95.3 has upto 225) 5 | #ifndef __NR_futex 6 | #define __NR_futex (__NR_SYSCALL_BASE+240) 7 | #endif 8 | #ifndef __NR_exit_group 9 | #define __NR_exit_group (__NR_SYSCALL_BASE+248) 10 | #endif 11 | #ifndef __NR_clock_gettime 12 | #define __NR_clock_gettime (__NR_SYSCALL_BASE+263) 13 | #endif 14 | 15 | .global sys_cacheflush @ const void *start_addr, const void *end_addr 16 | sys_cacheflush: 17 | mov r2, #0 18 | #ifdef __ARM_EABI__ 19 | /* EABI version */ 20 | str r7, [sp, #-4]! 21 | mov r7, #(__ARM_NR_cacheflush & 0xff) 22 | #if (__ARM_NR_cacheflush & 0x00ff00) 23 | orr r7, r7, #(__ARM_NR_cacheflush & 0x00ff00) 24 | #endif 25 | #if (__ARM_NR_cacheflush & 0xff0000) 26 | orr r7, r7, #(__ARM_NR_cacheflush & 0xff0000) 27 | #endif 28 | swi 0 29 | ldr r7, [sp], #4 30 | #else 31 | /* OABI */ 32 | swi __ARM_NR_cacheflush 33 | #endif 34 | bx lr 35 | 36 | 37 | #ifdef __ARM_EABI__ 38 | #error hm? 39 | #endif 40 | 41 | .global g_syscall 42 | g_syscall: 43 | mov r12, sp 44 | stmfd sp!, {r4, r5, r6} 45 | ldmia r12, {r4, r5, r6} 46 | swi __NR_syscall 47 | ldmfd sp!, {r4, r5, r6} 48 | 49 | .global g_syscall_error 50 | g_syscall_error: 51 | cmn r0, #4096 52 | bxcc lr 53 | stmfd sp!, {r4, lr} 54 | rsb r4, r0, #0 55 | bl __errno_location 56 | str r4, [r0] 57 | mov r0, #-1 58 | ldmfd sp!, {r4, pc} 59 | 60 | .global g_clone 61 | g_clone: 62 | ldr r12,[sp, #4] @ arg6 - convenience func ptr 63 | str r4, [sp, #-4]! 64 | ldr r4, [sp, #4] @ arg5 65 | swi __NR_clone 66 | tst r0, r0 67 | bxeq r12 @ child 68 | cmn r0, #4096 69 | ldr r4, [sp], #4 70 | bcs g_syscall_error 71 | bx lr 72 | 73 | @ raw - no errno 74 | .macro raw_syscall_easy name nr 75 | .global \name 76 | \name: 77 | swi \nr 78 | bx lr 79 | .endm 80 | 81 | .macro raw_syscall name nr 82 | .global \name 83 | \name: 84 | mov r12, sp 85 | stmfd sp!, {r4, r5, r6, lr} 86 | ldmia r12, {r4, r5, r6} 87 | swi \nr 88 | ldmfd sp!, {r4, r5, r6, pc} 89 | .endm 90 | 91 | raw_syscall_easy g_open_raw, __NR_open 92 | raw_syscall_easy g_read_raw, __NR_read 93 | raw_syscall_easy g_write_raw, __NR_write 94 | raw_syscall g_mmap2_raw, __NR_mmap2 95 | raw_syscall_easy g_munmap_raw, __NR_munmap 96 | raw_syscall_easy g_ioctl_raw, __NR_ioctl 97 | raw_syscall_easy g_close_raw, __NR_close 98 | raw_syscall_easy g_chdir_raw, __NR_chdir 99 | raw_syscall_easy g_mkdir_raw, __NR_mkdir 100 | raw_syscall_easy g_futex_raw, __NR_futex 101 | raw_syscall_easy g_nanosleep_raw, __NR_nanosleep 102 | raw_syscall_easy g_readlink_raw, __NR_readlink 103 | raw_syscall_easy g_execve_raw, __NR_execve 104 | raw_syscall_easy g_clock_gettime_raw, __NR_clock_gettime 105 | raw_syscall_easy g_rt_sigprocmask_raw, __NR_rt_sigprocmask 106 | raw_syscall_easy g_personality, __NR_personality 107 | raw_syscall_easy g_exit_group_raw, __NR_exit_group 108 | -------------------------------------------------------------------------------- /loader/syscalls.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct timespec; 4 | 5 | void sys_cacheflush(const void *start_addr, const void *end_addr); 6 | 7 | long g_syscall(long number, ...); 8 | 9 | // arg6 is func ptr, for convenience 10 | long g_clone(unsigned long flags, void *child_stack, ...); 11 | 12 | long g_syscall_error(long kret); 13 | 14 | // raw - no errno handling 15 | long g_open_raw(const char *pathname, int flags, ...); 16 | long g_read_raw(int fd, void *buf, size_t count); 17 | long g_write_raw(int fd, const void *buf, size_t count); 18 | long g_mmap2_raw(void *addr, size_t length, int prot, int flags, 19 | int fd, off_t offset); 20 | long g_munmap_raw(void *addr, size_t length); 21 | long g_ioctl_raw(int fd, unsigned long request, ...); 22 | long g_close_raw(int fd); 23 | long g_chdir_raw(const char *path); 24 | long g_mkdir_raw(const char *pathname, unsigned int mode); 25 | long g_futex_raw(int *uaddr, int op, int val, 26 | const struct timespec *timeout); 27 | long g_nanosleep_raw(const struct timespec *req, struct timespec *rem); 28 | long g_readlink_raw(const char *pathname, char *buf, size_t bufsiz); 29 | long g_execve_raw(const char *filename, char * const argv[], 30 | char * const envp[]); 31 | long g_clock_gettime_raw(int clk_id, const struct timespec *tp); 32 | long g_rt_sigprocmask_raw(int how, const void *set, void *oldset, 33 | size_t sigsetsize); 34 | long g_personality(long persona); 35 | long __attribute__((noreturn)) 36 | g_exit_group_raw(int status); 37 | 38 | -------------------------------------------------------------------------------- /loader/tools/mcut.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | FILE *fi; 6 | int c, t; 7 | 8 | if (argc < 4) { 9 | printf("usage:\n%s \n", argv[0]); 10 | return 1; 11 | } 12 | 13 | fi = fopen(argv[1], "rb"); 14 | fseek(fi, strtoul(argv[2], NULL, 0), SEEK_SET); 15 | c = atoi(argv[3]); 16 | 17 | while (c--) { 18 | fread(&t, 1, 4, fi); 19 | printf("0x%08x, ", t); 20 | } 21 | printf("\n"); 22 | 23 | return 0; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /loader/tools/static.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static int open_(const char *name) 14 | { 15 | int fd = open(name, O_RDWR); 16 | if (fd < 0) { 17 | fprintf(stderr, "%s: ", name); 18 | perror("open"); 19 | return 1; 20 | } 21 | 22 | return fd; 23 | } 24 | 25 | int main(int argc, char *argv[]) 26 | { 27 | volatile void *memregs; 28 | void *fbmem; 29 | int memdev, fbdev; 30 | int i; 31 | 32 | printf("hi, home=%s\n", getenv("HOME")); 33 | 34 | for (i = 0; i < argc; i++) 35 | printf("%d \"%s\"\n", i, argv[i]); 36 | 37 | memdev = open_("/dev/mem"); 38 | fbdev = open_("/dev/fb0"); 39 | 40 | memregs = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000); 41 | fbmem = mmap(NULL, 320*240*2, PROT_READ|PROT_WRITE, MAP_SHARED, fbdev, 0); 42 | 43 | ioctl(-1, 0); 44 | signal(7, SIG_DFL); 45 | system("buhbuh"); 46 | execl("bah", "bah", NULL); 47 | execlp("bah", "bah", NULL); 48 | if (argc == 1000) 49 | fork(); 50 | chdir("wuhahaha!"); 51 | usleep(1); 52 | // tcgetattr(-1, NULL); 53 | // tcsetattr(-1, 0, NULL); 54 | 55 | #if 1 56 | for (i = 0; i < 2; i++) 57 | printf("%02x %04x %08x\n", ((char *)memregs)[0x2011], 58 | ((short *)memregs)[0x1198/2], ((int *)memregs)[0xbcdc/4]); 59 | #endif 60 | memset(fbmem, 0xff, 320*240*2); 61 | 62 | sleep(10); 63 | 64 | return 0; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /make_caanoo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | outc=out_caanoo 6 | export out=${outc}/ginge 7 | # all same as wiz, except menu 8 | export tag=_wiz 9 | 10 | rm -rf ${out} 11 | dist/make_cmn.sh 12 | cp gp2xmenu/gp2xmenu_caanoo ${out}/gp2xmenu 13 | mkdir -p ${out}/tools ${out}/lib 14 | cp dist/ginge.gpe ${out}/ 15 | cp dist/ginge.ini ${outc}/ 16 | cp dist/ginge26.png ${out}/ginge.png 17 | cp dist/ginge_banner.png ${out}/ 18 | cp dist/ginge_dyn_eabi.sh ${out}/ginge_dyn.sh 19 | cp tools/cramfsck_eabi ${out}/tools/cramfsck 20 | cp tools/warm_2.6.24.ko ${out}/tools/ 21 | cp -r lib ${out}/ 22 | 23 | cd ${outc}/ 24 | rm ../ginge_caanoo.zip 2> /dev/null || true 25 | zip -9r ../ginge_caanoo.zip * 26 | 27 | -------------------------------------------------------------------------------- /make_pnd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | pnd_make=$HOME/dev/pnd/src/pandora-libraries/testdata/scripts/pnd_make.sh 4 | 5 | set -e 6 | 7 | export out=out_pnd 8 | export tag=_pnd 9 | 10 | dist/make_cmn.sh 11 | cp gp2xmenu/gp2xmenu${tag} ${out}/gp2xmenu 12 | mkdir -p ${out}/tools 13 | cp dist/ginge.sh ${out}/ 14 | cp dist/ginge_dyn_eabi.sh ${out}/ginge_dyn.sh 15 | cp tools/cramfsck_eabi ${out}/tools/cramfsck 16 | cp -r lib ${out}/ 17 | 18 | $pnd_make -p ginge.pnd -d ${out} -x dist/ginge.pxml -c -i dist/ginge60.png 19 | -------------------------------------------------------------------------------- /make_wiz.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | export out=out_wiz 6 | export tag=_wiz 7 | 8 | dist/make_cmn.sh 9 | cp gp2xmenu/gp2xmenu${tag} ${out}/gp2xmenu 10 | mkdir -p ${out}/tools ${out}/lib 11 | cp dist/ginge.gpe ${out}/ 12 | cp dist/ginge32.png ${out}/ginge.png 13 | cp dist/ginge_dyn_oabi.sh ${out}/ginge_dyn.sh 14 | cp lib/libSDL-1.2.so.0.7.0 ${out}/lib/libSDL-1.2.so.0 15 | cp tools/cramfsck_oabi ${out}/tools/cramfsck 16 | cp tools/warm_2.6.24.ko ${out}/tools/ 17 | 18 | dd if=/dev/zero of=${out}/swapfile bs=1M count=16 19 | 20 | cd ${out} 21 | rm ../ginge_wiz.zip 2> /dev/null || true 22 | zip -9r ../ginge_wiz.zip * 23 | -------------------------------------------------------------------------------- /prep/Makefile: -------------------------------------------------------------------------------- 1 | CC = $(CROSS_COMPILE)gcc 2 | CFLAGS += -Wall -O2 3 | LDFLAGS = -s -O2 4 | ifdef WIZ 5 | CFLAGS += -DWIZ 6 | TAG = _wiz 7 | endif 8 | ifdef PND 9 | CFLAGS += -DPND 10 | TAG = _pnd 11 | endif 12 | 13 | vpath %.c = ../common/ 14 | 15 | TARGET = ginge_prep$(TAG) 16 | OBJS += main.o host_fb.o cmn.o 17 | 18 | all: $(TARGET) 19 | 20 | $(TARGET): $(OBJS) 21 | $(CC) -o $@ $^ $(LDFLAGS) 22 | 23 | clean: 24 | $(RM) $(TARGET) $(OBJS) 25 | -------------------------------------------------------------------------------- /prep/font.c: -------------------------------------------------------------------------------- 1 | static const unsigned char fontdata8x8[64*16] = 2 | { 3 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 4 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 5 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 6 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 7 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 8 | 0x3C,0x42,0x99,0xBD,0xBD,0x99,0x42,0x3C,0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C, 9 | 0xFE,0x82,0x8A,0xD2,0xA2,0x82,0xFE,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x00, 10 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00, 11 | 0x80,0xC0,0xF0,0xFC,0xF0,0xC0,0x80,0x00,0x01,0x03,0x0F,0x3F,0x0F,0x03,0x01,0x00, 12 | 0x18,0x3C,0x7E,0x18,0x7E,0x3C,0x18,0x00,0xEE,0xEE,0xEE,0xCC,0x00,0xCC,0xCC,0x00, 13 | 0x00,0x00,0x30,0x68,0x78,0x30,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00, 14 | 0x3C,0x66,0x7A,0x7A,0x7E,0x7E,0x3C,0x00,0x0E,0x3E,0x3A,0x22,0x26,0x6E,0xE4,0x40, 15 | 0x18,0x3C,0x7E,0x3C,0x3C,0x3C,0x3C,0x00,0x3C,0x3C,0x3C,0x3C,0x7E,0x3C,0x18,0x00, 16 | 0x08,0x7C,0x7E,0x7E,0x7C,0x08,0x00,0x00,0x10,0x3E,0x7E,0x7E,0x3E,0x10,0x00,0x00, 17 | 0x58,0x2A,0xDC,0xC8,0xDC,0x2A,0x58,0x00,0x24,0x66,0xFF,0xFF,0x66,0x24,0x00,0x00, 18 | 0x00,0x10,0x10,0x38,0x38,0x7C,0xFE,0x00,0xFE,0x7C,0x38,0x38,0x10,0x10,0x00,0x00, 19 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x18,0x00,0x18,0x18,0x00, 20 | 0x6C,0x6C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00, 21 | 0x10,0x38,0x60,0x38,0x0C,0x78,0x10,0x00,0x40,0xA4,0x48,0x10,0x24,0x4A,0x04,0x00, 22 | 0x18,0x34,0x18,0x3A,0x6C,0x66,0x3A,0x00,0x18,0x18,0x20,0x00,0x00,0x00,0x00,0x00, 23 | 0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x00,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x00, 24 | 0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00, 25 | 0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00, 26 | 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00, 27 | 0x38,0x4C,0xC6,0xC6,0xC6,0x64,0x38,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00, 28 | 0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00,0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00, 29 | 0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00,0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00, 30 | 0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00, 31 | 0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00,0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00, 32 | 0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30, 33 | 0x1C,0x38,0x70,0xE0,0x70,0x38,0x1C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00, 34 | 0x70,0x38,0x1C,0x0E,0x1C,0x38,0x70,0x00,0x7C,0xC6,0xC6,0x1C,0x18,0x00,0x18,0x00, 35 | 0x3C,0x42,0x99,0xA1,0xA5,0x99,0x42,0x3C,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00, 36 | 0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00, 37 | 0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00,0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00, 38 | 0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00,0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00, 39 | 0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00, 40 | 0x06,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0x00, 41 | 0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00, 42 | 0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, 43 | 0xFC,0xC6,0xC6,0xC6,0xFC,0xC0,0xC0,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x7A,0x00, 44 | 0xFC,0xC6,0xC6,0xCE,0xF8,0xDC,0xCE,0x00,0x78,0xCC,0xC0,0x7C,0x06,0xC6,0x7C,0x00, 45 | 0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, 46 | 0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00,0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00, 47 | 0xC6,0xEE,0x3C,0x38,0x7C,0xEE,0xC6,0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00, 48 | 0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00, 49 | 0x60,0x60,0x30,0x18,0x0C,0x06,0x06,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00, 50 | 0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF, 51 | 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3C,0x00, 52 | 0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x00, 53 | 0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x3C,0x00, 54 | 0x1C,0x30,0x78,0x30,0x30,0x30,0x30,0x00,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x3C, 55 | 0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x00, 56 | 0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x38,0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00, 57 | 0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0xEC,0xFE,0xFE,0xFE,0xD6,0xC6,0x00, 58 | 0x00,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00, 59 | 0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x06, 60 | 0x00,0x7E,0x70,0x60,0x60,0x60,0x60,0x00,0x00,0x3C,0x60,0x3C,0x06,0x66,0x3C,0x00, 61 | 0x30,0x78,0x30,0x30,0x30,0x30,0x1C,0x00,0x00,0x66,0x66,0x66,0x66,0x6E,0x3E,0x00, 62 | 0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x7C,0x6C,0x00, 63 | 0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C, 64 | 0x00,0x7E,0x0C,0x18,0x30,0x60,0x7E,0x00,0x0E,0x18,0x0C,0x38,0x0C,0x18,0x0E,0x00, 65 | 0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x70,0x18,0x30,0x1C,0x30,0x18,0x70,0x00, 66 | 0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x10,0x28,0x10,0x54,0xAA,0x44,0x00,0x00, 67 | }; 68 | 69 | -------------------------------------------------------------------------------- /prep/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GINGE - GINGE Is Not Gp2x Emulator 3 | * (C) notaz, 2010-2011 4 | * 5 | * This work is licensed under the MAME license, see COPYING file for details. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "../common/host_fb.h" 17 | #include "../common/cmn.h" 18 | 19 | #define PFX "ging_prep: " 20 | #define LOADER_STATIC "ginge_sloader" 21 | #define LOADER_DYNAMIC "ginge_dyn.sh" 22 | #define LAUNCHER "gp2xmenu" 23 | 24 | #ifdef PND 25 | #define WRAP_APP "op_runfbapp " 26 | #else 27 | #define WRAP_APP "" 28 | #endif 29 | 30 | #include "font.c" 31 | 32 | static void *fb_mem; 33 | static int fb_stride; 34 | static int fb_x, fb_y; 35 | static int init_done; 36 | 37 | static char *sskip(char *p) 38 | { 39 | while (p && *p && isspace(*p)) 40 | p++; 41 | return p; 42 | } 43 | 44 | static char *cskip(char *p) 45 | { 46 | while (p && *p && !isspace(*p)) 47 | p++; 48 | return p; 49 | } 50 | 51 | static void fb_text_exit(void) 52 | { 53 | if (!init_done) 54 | return; 55 | 56 | host_video_finish(); 57 | init_done = 0; 58 | } 59 | 60 | static void fb_text_init(void) 61 | { 62 | int ret = host_video_init(&fb_stride, 1); 63 | if (ret == 0) 64 | fb_mem = host_video_flip(); 65 | fb_x = 4; 66 | fb_y = 4; 67 | init_done = 1; 68 | atexit(fb_text_exit); 69 | } 70 | 71 | static void fb_syms_out(void *fbi, int x, int y, int dotsz, int stride, const char *text, int count) 72 | { 73 | int v = -1, n = 0, *p; 74 | int i, l; 75 | char *fb; 76 | 77 | fb = (char *)fbi + x * dotsz + y * stride; 78 | 79 | for (i = 0; i < count; i++) 80 | { 81 | for (l = 0; l < 8; l++) 82 | { 83 | #define pix(fdmask,add) \ 84 | p = (fontdata8x8[((text[i])*8)+l] & fdmask) ? &v : &n; \ 85 | memcpy(fb + l*stride + add*dotsz, p, dotsz) 86 | pix(0x80, 0); 87 | pix(0x40, 1); 88 | pix(0x20, 2); 89 | pix(0x10, 3); 90 | pix(0x08, 4); 91 | pix(0x04, 5); 92 | pix(0x02, 6); 93 | pix(0x01, 7); 94 | #undef pix 95 | } 96 | fb += dotsz * 8; 97 | } 98 | } 99 | 100 | // FIXME: y overrun 101 | static void fb_text_out(char *text) 102 | { 103 | int dotsz = 2, w = 320; // hardcoded for now 104 | char *p, *pe; 105 | int l; 106 | 107 | if (!init_done) 108 | fb_text_init(); 109 | 110 | if (fb_mem == NULL) 111 | return; 112 | 113 | p = text; 114 | while (*p) { 115 | for (; *p && isspace(*p); p++) { 116 | if (*p == '\n' || fb_x + dotsz * 8 > w) { 117 | fb_x = 4; 118 | fb_y += 8; 119 | } 120 | if (*p >= 0x20) 121 | fb_x += 8; 122 | } 123 | 124 | pe = cskip(p); 125 | l = pe - p; 126 | if (fb_x + 8 * l > w) { 127 | fb_x = 4; 128 | fb_y += 8; 129 | } 130 | fb_syms_out(fb_mem, fb_x, fb_y, dotsz, fb_stride, p, l); 131 | fb_x += 8 * l; 132 | p = pe; 133 | } 134 | } 135 | 136 | static void fbprintf(int is_err, const char *format, ...) 137 | { 138 | va_list ap; 139 | char buff[512]; 140 | 141 | va_start(ap, format); 142 | vsnprintf(buff, sizeof(buff), format, ap); 143 | va_end(ap); 144 | fputs(buff, is_err ? stderr : stdout); 145 | 146 | fb_text_out(buff); 147 | } 148 | 149 | #define msg(fmt, ...) fbprintf(0, fmt, ##__VA_ARGS__) 150 | #define err(fmt, ...) fbprintf(1, fmt, ##__VA_ARGS__) 151 | 152 | static int id_elf(const char *fname) 153 | { 154 | Elf32_Ehdr hdr; 155 | Elf32_Phdr *phdr = NULL; 156 | FILE *fi; 157 | int i, ret = 0; 158 | 159 | fi = fopen(fname, "rb"); 160 | if (fi == NULL) { 161 | err("open %s: ", fname); 162 | perror(""); 163 | return -1; 164 | } 165 | 166 | if (fread(&hdr, 1, sizeof(hdr), fi) != sizeof(hdr)) 167 | goto out; 168 | 169 | if (memcmp(hdr.e_ident, ELFMAG "\x01\x01", SELFMAG + 2) != 0) 170 | goto out; 171 | 172 | if (hdr.e_phentsize != sizeof(Elf32_Phdr) || hdr.e_phnum == 0) 173 | goto out; 174 | 175 | phdr = malloc(hdr.e_phnum * hdr.e_phentsize); 176 | if (phdr == NULL) 177 | goto out; 178 | 179 | if (fread(phdr, hdr.e_phentsize, hdr.e_phnum, fi) != hdr.e_phnum) 180 | goto out; 181 | 182 | ret = 1; 183 | 184 | // do what 'file' does - check for PT_INTERP in program headers 185 | for (i = 0; i < hdr.e_phnum; i++) { 186 | if (phdr[i].p_type == PT_INTERP) { 187 | ret = 2; 188 | break; 189 | } 190 | } 191 | 192 | out: 193 | fclose(fi); 194 | free(phdr); 195 | return ret; 196 | } 197 | 198 | static void dump_args(FILE *fout, char * const argv[]) 199 | { 200 | const char *p; 201 | int i; 202 | 203 | for (i = 0; argv[i] != NULL; i++) { 204 | if (i != 0) 205 | fputc(' ', fout); 206 | fputc('"', fout); 207 | 208 | for (p = argv[i]; *p != 0; p++) { 209 | if (*p == '"') 210 | fputc('\\', fout); 211 | fputc(*p, fout); 212 | } 213 | 214 | fputc('"', fout); 215 | } 216 | } 217 | 218 | static char *get_arg(char *d, size_t size, char *p) 219 | { 220 | char *pe; 221 | int len; 222 | 223 | p = sskip(p); 224 | pe = cskip(p); 225 | len = pe - p; 226 | 227 | if (len > size - 1) { 228 | err(PFX "get_arg: buff to small: %d/%d\n", len, size); 229 | len = size - 1; 230 | } 231 | strncpy(d, p, len); 232 | d[len] = 0; 233 | 234 | return sskip(pe); 235 | } 236 | 237 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 238 | 239 | #define CB_ENTRY(x) { x, sizeof(x) - 1 } 240 | const struct { 241 | const char *name; 242 | int len; 243 | } conv_blacklist[] = { 244 | CB_ENTRY("insmod"), 245 | CB_ENTRY("modprobe"), 246 | CB_ENTRY("umount"), 247 | CB_ENTRY("./cpuctrl_tiny"), 248 | }; 249 | 250 | static int cmd_in_blacklist(char *cmd) 251 | { 252 | int i; 253 | 254 | cmd = sskip(cmd); 255 | for (i = 0; i < ARRAY_SIZE(conv_blacklist); i++) 256 | if (strncmp(cmd, conv_blacklist[i].name, conv_blacklist[i].len) == 0) 257 | return 1; 258 | 259 | return 0; 260 | } 261 | 262 | int main(int argc, char *argv[]) 263 | { 264 | static const char out_script[] = "/tmp/ginge_conv.sh"; 265 | char root_path[512], cwd[512]; 266 | char **argv_app = NULL; 267 | int have_cramfs = 0; 268 | int rerun_gp2xmenu = 1; 269 | int quit_if_no_app = 0; 270 | FILE *fin, *fout; 271 | int i, ret; 272 | 273 | for (i = 1; i < argc && argv[i][0] == '-' && argv[i][1] == '-'; i++) { 274 | if (strcmp(argv[i], "--cleanup") == 0) { 275 | // as loader may crash eny time, restore screen for the menu 276 | host_video_init(NULL, 1); 277 | host_video_finish(); 278 | quit_if_no_app = 1; 279 | continue; 280 | } 281 | if (strcmp(argv[i], "--nomenu") == 0) { 282 | rerun_gp2xmenu = 0; 283 | continue; 284 | } 285 | if (strcmp(argv[i], "--") == 0) { 286 | i++; 287 | break; 288 | } 289 | 290 | fprintf(stderr, PFX "ignoring unknown option \"%s\"\n", argv[i]); 291 | } 292 | 293 | if (argc <= i) { 294 | if (quit_if_no_app) 295 | return 0; 296 | err("usage: %s [opts] [args]\n", argv[0]); 297 | err(" --cleanup - restore framebuffer state\n"); 298 | err(" --nomenu - don't run menu on exit\n"); 299 | return 1; 300 | } 301 | argv_app = &argv[i]; 302 | 303 | if (getcwd(cwd, sizeof(cwd)) == NULL) { 304 | err(PFX "failed to get cwd\n"); 305 | return 1; 306 | } 307 | 308 | ret = make_local_path(root_path, sizeof(root_path), ""); 309 | if (ret != 0) { 310 | err(PFX "failed to generate root path\n"); 311 | return 1; 312 | } 313 | 314 | fout = fopen(out_script, "w"); 315 | if (fout == NULL) { 316 | perror("can't open output script"); 317 | return 1; 318 | } 319 | 320 | fprintf(fout, "#!/bin/sh\n"); 321 | 322 | ret = id_elf(argv_app[0]); 323 | if (ret == 1 || ret == 2) { 324 | if (cmd_in_blacklist(argv_app[0])) { 325 | fprintf(stderr, "blacklisted: %s\n", argv_app[0]); 326 | goto no_in_script; 327 | } 328 | } 329 | 330 | switch (ret) { 331 | case 0: 332 | break; 333 | 334 | case 1: 335 | fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC); 336 | dump_args(fout, argv_app); 337 | fprintf(fout, "\n"); 338 | goto no_in_script; 339 | 340 | case 2: 341 | fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path); 342 | dump_args(fout, argv_app); 343 | fprintf(fout, "\n"); 344 | goto no_in_script; 345 | 346 | default: 347 | return 1; 348 | } 349 | 350 | // assume script 351 | fin = fopen(argv_app[0], "r"); 352 | if (fin == NULL) 353 | return 1; 354 | 355 | while (1) { 356 | char buff[512], fname[512], *p, *p2; 357 | 358 | p = fgets(buff, sizeof(buff), fin); 359 | if (p == NULL) 360 | break; 361 | p = sskip(p); 362 | 363 | if (p[0] == '#' && p[1] == '!') 364 | continue; 365 | 366 | if (*p == 0) { 367 | fputs("\n", fout); 368 | continue; 369 | } 370 | 371 | // things we are sure we want to pass 372 | if (*p == '#' || strncmp(p, "export ", 7) == 0) 373 | goto pass; 374 | 375 | // hmh.. 376 | if (strncmp(p, "exec ", 5) == 0) 377 | p = sskip(p + 5); 378 | 379 | // blacklist some stuff 380 | if (strncmp(p, "/sbin/", 6) == 0) 381 | p2 = p + 6; 382 | else if (strncmp(p, "/bin/", 5) == 0) 383 | p2 = p + 5; 384 | else 385 | p2 = p; 386 | if (strncmp(p2, "mount ", 6) == 0) { 387 | p2 = sskip(p2 + 6); 388 | // cramfs stuff? 389 | if (strstr(p2, "cramfs")) { 390 | while (*p2 == '-') { 391 | // skip option 392 | p2 = sskip(cskip(p2)); 393 | p2 = sskip(cskip(p2)); 394 | } 395 | if (*p2 == 0) { 396 | err(PFX "cramfs: missing mount file in \"%s\"?\n", p); 397 | continue; 398 | } 399 | p2 = get_arg(fname, sizeof(fname), p2); 400 | if (*p2 == 0) { 401 | err(PFX "cramfs: missing mount point in \"%s\"?\n", p); 402 | continue; 403 | } 404 | get_arg(buff, sizeof(buff), p2); 405 | fprintf(fout, "if [ `ls %s | wc -l` -eq 0 ]; then\n", buff); 406 | fprintf(fout, " rmdir \"%s\"\n", buff); // cramfsck doesn't want it 407 | fprintf(fout, " %stools/cramfsck -x \"%s\" \"%s\"\n", root_path, buff, fname); 408 | fprintf(fout, "fi\n"); 409 | have_cramfs = 1; 410 | } 411 | continue; 412 | } 413 | if (cmd_in_blacklist(p2)) 414 | continue; 415 | 416 | // cd? 417 | if (strncmp(p, "cd ", 3) == 0) { 418 | get_arg(fname, sizeof(fname), p + 3); 419 | if (strncmp(fname, "/usr/gp2x", 9) == 0) 420 | continue; 421 | ret = chdir(fname); 422 | if (ret != 0) { 423 | err("%s: ", fname); 424 | perror(""); 425 | } 426 | } 427 | 428 | // trying to run something from cwd? 429 | if ((p[0] == '.' && p[1] == '/') || *p == '/') { 430 | get_arg(fname, sizeof(fname), p); 431 | p2 = strrchr(fname, '/'); 432 | if (p2 != NULL && strcmp(p2 + 1, "gp2xmenu") == 0) 433 | continue; 434 | 435 | ret = id_elf(fname); 436 | switch (ret) { 437 | case 1: 438 | printf(PFX "prefixing as static: %s", p); 439 | fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC); 440 | break; 441 | 442 | case 2: 443 | printf(PFX "prefixing as dynamic: %s", p); 444 | fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path); 445 | break; 446 | 447 | default: 448 | break; 449 | } 450 | } 451 | 452 | pass: 453 | fputs(p, fout); 454 | } 455 | 456 | fclose(fin); 457 | 458 | no_in_script: 459 | #ifdef WIZ 460 | fprintf(fout, "sync\n"); 461 | // since we don't know if loader manages to do proper cleanup, 462 | // need to wait for it's threads to die 463 | fprintf(fout, "sleep 1\n"); 464 | fprintf(fout, "%sginge_prep --cleanup\n", root_path); 465 | #endif 466 | if (rerun_gp2xmenu) { 467 | fprintf(fout, "cd %s\n", root_path); 468 | fprintf(fout, "exec %s%s\n", root_path, LAUNCHER); 469 | } 470 | 471 | fclose(fout); 472 | 473 | //msg("starting script..\n"); 474 | if (have_cramfs) { 475 | msg("\nsome files need to be unpacked, this may tike a few minutes.\n"); 476 | #ifdef PND 477 | msg("Please wait at least while SD LED is active.\n"); 478 | #endif 479 | } 480 | system("echo ---; cat /tmp/ginge_conv.sh; echo ---"); 481 | chmod(out_script, S_IRWXU|S_IRWXG|S_IRWXO); 482 | chdir(cwd); 483 | fb_text_exit(); 484 | execlp(out_script, out_script, NULL); 485 | perror("run out_script"); 486 | 487 | return 1; 488 | } 489 | 490 | // vim:shiftwidth=2:expandtab 491 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | 2 | GINGE - Ginge Is Not GP2X Emulator 3 | release 4 4 | 5 | (C) notaz, 2010-2011 6 | http://notaz.gp2x.de/ 7 | 8 | 9 | About 10 | ----- 11 | 12 | Ginge is an application that can run many GP2X F100/F200, Wiz games and 13 | programs on other ARM Linux platforms, which currently includes Pandora, 14 | Caanoo and Wiz itself. It is not a full hardware emulator like MAME, PicoDrive 15 | or similar, it does not emulate the CPU. It can be considered as compatibility 16 | layer similar to Wine on PC Linux, however it does emulate small portion of 17 | MMSP2 and Pollux system-on-chips. It operates by hooking certain system calls 18 | and using realtime patching of code that accesses memory mapped hardware 19 | directly. 20 | 21 | 22 | Usage 23 | ----- 24 | 25 | Ginge comes with a launcher that is started when you run Ginge. The launcher 26 | can then be used to start GP2X software, which will either run if it's 27 | compatible, or just return back to the menu if it is not. In some cases it 28 | might hang though. 29 | 30 | Keys are mapped to corresponding keys pandora, Wiz and Caanoo , except: 31 | 32 | Key Pandora Wiz Caanoo 33 | Stick Push 1 unmapped Stick Push 34 | Volume up/down '.', ',' Volume up/down Home+I/Home+II 35 | 36 | On pandora pressing 'q' will exit the menu or try to kill current application. 37 | On Cannoo Home+Y tries to kill current application. 38 | 39 | 40 | Structure 41 | --------- 42 | 43 | Ginge actually consists of 4 independent executables and a few scripts: 44 | 45 | + ginge_sloader - loader of static executables 46 | + ginge_dyn - dynamic executable handler 47 | + ginge_prep - .gpe parser that selects the right handler from above 48 | + gp2xmenu - the launcher/menu program 49 | + ginge_dyn.sh - environment setup script for ginge_dyn 50 | + ginge.sh/gpe - menu launcher script 51 | 52 | The menu is optional and can be replaced or bypassed completely. The only thing 53 | it does is running ginge_prep on GP2X .gpe program, ginge_prep handles the rest. 54 | 55 | 56 | Changelog 57 | --------- 58 | 59 | r4 60 | + ginge now runs on Caanoo 61 | * minor fixes in path handling 62 | 63 | r3 64 | * improved exec handling, mostly for gpecomp. 65 | + added preliminary Wiz support, pcsx4all works. 66 | * Wiz: since some stuff is written to /tmp, mount tmpfs there when starting 67 | to avoid wearing down flash. 68 | 69 | r2 70 | * improved exit handling 71 | * Wiz: should now return to Wiz menu after gp2xmenu exit 72 | 73 | r1 - initial release 74 | + icons provided by Inder 75 | 76 | 77 | License 78 | ------- 79 | 80 | gp2xmenu is based on GPH GPL source (http://www.gnu.org/licenses/gpl.html). 81 | Source is available at http://notaz.gp2x.de/releases/ginge/gp2xmenu.tar.bz2 82 | It is separate program and not linked to the remaining portion in any way. 83 | 84 | Ginge may come with some libraries. Those libraries are unmodified copies 85 | of ones found in root filesystems in GP2X and Wiz and are included to more 86 | accurately reproduce environment found on GP2X. Their source code may or may 87 | not be available, I did not use it, but whatever I found is mirrored here: 88 | http://notaz.gp2x.de/downloads/gp2x/src/410_all/ 89 | 90 | Remaining portion is released under the MAME license, see COPYING file for 91 | details. 92 | -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | - extract text 2 | - giana 3 | - odanata - no sound 4 | - touchscreen 5 | 6 | - FBA works on Wiz, not on Caanoo? 7 | --------------------------------------------------------------------------------