annotate menu.c @ 989:d70000fdff0b

Implemented IR and undefined bits of info word for address error exception frames
author Michael Pavone <pavone@retrodev.com>
date Wed, 27 Apr 2016 21:39:17 -0700
parents 4899d3ae37b3
children 51885857c019
rev   line source
pavone@866 1 #include <stdint.h>
pavone@866 2 #include <stdlib.h>
pavone@866 3 #include <string.h>
pavone@866 4 #include <stdio.h>
pavone@934 5 #include <errno.h>
pavone@866 6 #include "blastem.h"
pavone@866 7 #include "menu.h"
pavone@866 8 #include "backend.h"
pavone@866 9 #include "util.h"
pavone@961 10 #include "gst.h"
pavone@961 11 #include "m68k_internal.h" //needed for get_native_address_trans, should be eliminated once handling of PC is cleaned up
pavone@866 12
pavone@866 13
pavone@866 14 uint16_t menu_read_w(uint32_t address, void * context)
pavone@866 15 {
pavone@866 16 //This should return the status of the last request with 0
pavone@866 17 //meaning either the request is complete or no request is pending
pavone@866 18 //in the current implementation, the operations happen instantly
pavone@866 19 //in emulated time so we can always return 0
pavone@866 20 return 0;
pavone@866 21 }
pavone@866 22
pavone@868 23 int menu_dir_sort(const void *a, const void *b)
pavone@868 24 {
pavone@868 25 const dir_entry *da, *db;
pavone@868 26 da = a;
pavone@868 27 db = b;
pavone@868 28 if (da->is_dir != db->is_dir) {
pavone@868 29 return db->is_dir - da->is_dir;
pavone@868 30 }
pavone@868 31 return strcasecmp(((dir_entry *)a)->name, ((dir_entry *)b)->name);
pavone@868 32 }
pavone@868 33
pavone@873 34 void copy_string_from_guest(m68k_context *m68k, uint32_t guest_addr, char *buf, size_t maxchars)
pavone@873 35 {
pavone@873 36 char *cur;
pavone@873 37 char *src = NULL;
pavone@873 38 for (cur = buf; cur < buf+maxchars; cur+=2, guest_addr+=2, src+=2)
pavone@873 39 {
pavone@873 40 if (!src || !(guest_addr & 0xFFFF)) {
pavone@873 41 //we may have walked off the end of a memory block, get a fresh native pointer
pavone@873 42 src = get_native_pointer(guest_addr, (void **)m68k->mem_pointers, &m68k->options->gen);
pavone@873 43 if (!src) {
pavone@873 44 break;
pavone@873 45 }
pavone@873 46 }
pavone@873 47 *cur = src[1];
pavone@873 48 cur[1] = *src;
pavone@873 49 if (!*src || !src[1]) {
pavone@873 50 break;
pavone@873 51 }
pavone@873 52 }
pavone@873 53 //make sure we terminate the string even if we did not hit a null terminator in the source
pavone@873 54 buf[maxchars-1] = 0;
pavone@873 55 }
pavone@873 56
pavone@957 57 void copy_to_guest(m68k_context *m68k, uint32_t guest_addr, char *src, size_t tocopy)
pavone@957 58 {
pavone@957 59 char *dst = NULL;
pavone@957 60 for (char *cur = src; cur < src+tocopy; cur+=2, guest_addr+=2, dst+=2)
pavone@957 61 {
pavone@957 62 if (!dst || !(guest_addr & 0xFFFF)) {
pavone@957 63 //we may have walked off the end of a memory block, get a fresh native pointer
pavone@957 64 dst = get_native_pointer(guest_addr, (void **)m68k->mem_pointers, &m68k->options->gen);
pavone@957 65 if (!dst) {
pavone@957 66 break;
pavone@957 67 }
pavone@957 68 }
pavone@958 69 dst[1] = *cur;
pavone@958 70 *dst = cur[1];
pavone@957 71 }
pavone@957 72 }
pavone@957 73
pavone@957 74 #define SAVE_INFO_BUFFER_SIZE (11*40)
pavone@957 75
pavone@882 76 #ifdef __ANDROID__
pavone@882 77 #include <SDL.h>
pavone@882 78 #include <jni.h>
pavone@882 79 char *get_external_storage_path()
pavone@882 80 {
pavone@882 81 static char *ret;
pavone@882 82 if (ret) {
pavone@882 83 return ret;
pavone@882 84 }
pavone@882 85 JNIEnv *env = SDL_AndroidGetJNIEnv();
pavone@882 86 if ((*env)->PushLocalFrame(env, 8) < 0) {
pavone@882 87 return NULL;
pavone@882 88 }
pavone@884 89
pavone@882 90 jclass Environment = (*env)->FindClass(env, "android/os/Environment");
pavone@884 91 jmethodID getExternalStorageDirectory =
pavone@882 92 (*env)->GetStaticMethodID(env, Environment, "getExternalStorageDirectory", "()Ljava/io/File;");
pavone@882 93 jobject file = (*env)->CallStaticObjectMethod(env, Environment, getExternalStorageDirectory);
pavone@882 94 if (!file) {
pavone@882 95 goto cleanup;
pavone@882 96 }
pavone@884 97
pavone@882 98 jmethodID getAbsolutePath = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file),
pavone@882 99 "getAbsolutePath", "()Ljava/lang/String;");
pavone@882 100 jstring path = (*env)->CallObjectMethod(env, file, getAbsolutePath);
pavone@884 101
pavone@882 102 char const *tmp = (*env)->GetStringUTFChars(env, path, NULL);
pavone@882 103 ret = strdup(tmp);
pavone@882 104 (*env)->ReleaseStringUTFChars(env, path, tmp);
pavone@884 105
pavone@882 106 cleanup:
pavone@882 107 (*env)->PopLocalFrame(env, NULL);
pavone@882 108 return ret;
pavone@882 109 }
pavone@882 110 #endif
pavone@882 111
pavone@972 112 #ifdef _WIN32
pavone@972 113 #define localtime_r(a,b) localtime(a)
pavone@972 114 #endif
pavone@972 115
pavone@866 116 void * menu_write_w(uint32_t address, void * context, uint16_t value)
pavone@866 117 {
pavone@866 118 m68k_context *m68k = context;
pavone@866 119 genesis_context *gen = m68k->system;
pavone@866 120 menu_context *menu = gen->extra;
pavone@866 121 if (!menu) {
pavone@866 122 gen->extra = menu = calloc(1, sizeof(menu_context));
pavone@874 123 menu->curpath = tern_find_path(config, "ui\0initial_path\0").ptrval;
pavone@875 124 if (menu->curpath) {
pavone@875 125 menu->curpath = strdup(menu->curpath);
pavone@875 126 } else {
pavone@875 127 #ifdef __ANDROID__
pavone@882 128 menu->curpath = strdup(get_external_storage_path());
pavone@875 129 #else
pavone@875 130 menu->curpath = strdup(get_home_dir());
pavone@875 131 #endif
pavone@875 132 }
pavone@866 133 }
pavone@866 134 if (menu->state) {
pavone@866 135 uint32_t dst = menu->latch << 16 | value;
pavone@866 136 switch (address >> 2)
pavone@866 137 {
pavone@866 138 case 0: {
pavone@866 139 size_t num_entries;
pavone@866 140 dir_entry *entries = get_dir_list(menu->curpath, &num_entries);
pavone@868 141 if (entries) {
pavone@868 142 qsort(entries, num_entries, sizeof(dir_entry), menu_dir_sort);
pavone@933 143 } else {
pavone@934 144 warning("Failed to open directory %s: %s\n", menu->curpath, strerror(errno));
pavone@868 145 }
pavone@868 146 uint8_t *dest;
pavone@866 147 for (size_t i = 0; i < num_entries; i++)
pavone@866 148 {
pavone@868 149 dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
pavone@866 150 if (!dest) {
pavone@866 151 break;
pavone@866 152 }
pavone@866 153 *(dest++) = entries[i].is_dir;
pavone@866 154 *(dest++) = 1;
pavone@866 155 dst += 2;
pavone@866 156 uint8_t term = 0;
pavone@866 157 for (char *cpos = entries[i].name; *cpos; cpos++)
pavone@866 158 {
pavone@866 159 dest[1] = *cpos;
pavone@866 160 dest[0] = cpos[1];
pavone@866 161 if (cpos[1]) {
pavone@866 162 cpos++;
pavone@866 163 } else {
pavone@866 164 term = 1;
pavone@866 165 }
pavone@866 166 dst += 2;
pavone@866 167 if (!(dst & 0xFFFF)) {
pavone@866 168 //we may have walked off the end of a memory block, get a fresh native pointer
pavone@866 169 dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
pavone@866 170 if (!dest) {
pavone@866 171 break;
pavone@866 172 }
pavone@866 173 } else {
pavone@866 174 dest += 2;
pavone@866 175 }
pavone@866 176 }
pavone@866 177 if (!term) {
pavone@866 178 *(dest++) = 0;
pavone@866 179 *dest = 0;
pavone@866 180 dst += 2;
pavone@866 181 }
pavone@866 182 }
pavone@868 183 //terminate list
pavone@868 184 dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
pavone@868 185 if (dest) {
pavone@868 186 *dest = dest[1] = 0;
pavone@868 187 free_dir_list(entries, num_entries);
pavone@868 188 }
pavone@868 189 break;
pavone@868 190 }
pavone@868 191 case 1: {
pavone@868 192 char buf[4096];
pavone@873 193 copy_string_from_guest(m68k, dst, buf, sizeof(buf));
pavone@870 194 if (!strcmp(buf, "..")) {
pavone@870 195 size_t len = strlen(menu->curpath);
pavone@870 196 while (len > 1) {
pavone@870 197 --len;
pavone@870 198 if (menu->curpath[len] == '/') {
pavone@870 199 menu->curpath[len] = 0;
pavone@870 200 break;
pavone@870 201 }
pavone@870 202 }
pavone@870 203 } else {
pavone@884 204 char *tmp = menu->curpath;
pavone@884 205 char const *pieces[] = {menu->curpath, "/", buf};
pavone@870 206 menu->curpath = alloc_concat_m(3, pieces);
pavone@884 207 free(tmp);
pavone@870 208 }
pavone@866 209 break;
pavone@866 210 }
pavone@873 211 case 2: {
pavone@873 212 char buf[4096];
pavone@873 213 copy_string_from_guest(m68k, dst, buf, sizeof(buf));
pavone@884 214 char const *pieces[] = {menu->curpath, "/", buf};
pavone@874 215 gen->next_rom = alloc_concat_m(3, pieces);
pavone@872 216 m68k->should_return = 1;
pavone@872 217 break;
pavone@957 218 }
pavone@949 219 case 3: {
pavone@954 220 switch (dst)
pavone@954 221 {
pavone@954 222 case 1:
pavone@954 223 m68k->should_return = 1;
pavone@954 224 gen->should_exit = 1;
pavone@954 225 break;
pavone@954 226 case 2:
pavone@954 227 m68k->should_return = 1;
pavone@954 228 break;
pavone@954 229 }
pavone@954 230
pavone@949 231 break;
pavone@949 232 }
pavone@957 233 case 4: {
pavone@957 234 char *buffer = malloc(SAVE_INFO_BUFFER_SIZE);
pavone@957 235 char *cur = buffer;
pavone@957 236 if (gen->next_context && gen->next_context->save_dir) {
pavone@957 237 char *end = buffer + SAVE_INFO_BUFFER_SIZE;
pavone@957 238 char slotfile[] = "slot_0.gst";
pavone@957 239 char const * parts[3] = {gen->next_context->save_dir, "/", slotfile};
pavone@957 240 struct tm ltime;
pavone@957 241 char *fname;
pavone@957 242 time_t modtime;
pavone@957 243 for (int i = 0; i < 10 && cur < end; i++)
pavone@957 244 {
pavone@957 245 slotfile[5] = i + '0';
pavone@957 246 fname = alloc_concat_m(3, parts);
pavone@957 247 modtime = get_modification_time(fname);
pavone@957 248 free(fname);
pavone@957 249 if (modtime) {
pavone@957 250 cur += snprintf(cur, end-cur, "Slot %d - ", i);
pavone@957 251 cur += strftime(cur, end-cur, "%c", localtime_r(&modtime, &ltime));
pavone@957 252
pavone@957 253 } else {
pavone@957 254 cur += snprintf(cur, end-cur, "Slot %d - EMPTY", i);
pavone@957 255 }
pavone@957 256 //advance past the null terminator for this entry
pavone@957 257 cur++;
pavone@957 258 }
pavone@957 259 if (cur < end) {
pavone@957 260 parts[2] = "quicksave.gst";
pavone@957 261 fname = alloc_concat_m(3, parts);
pavone@957 262 modtime = get_modification_time(fname);
pavone@957 263 free(fname);
pavone@957 264 if (modtime) {
pavone@958 265 cur += strftime(cur, end-cur, "Quick - %c", localtime_r(&modtime, &ltime));
pavone@958 266 } else if ((end-cur) > strlen("Quick - EMPTY")){
pavone@958 267 cur += strlen(strcpy(cur, "Quick - EMPTY"));
pavone@957 268 }
pavone@957 269 //advance past the null terminator for this entry
pavone@957 270 cur++;
pavone@957 271 if (cur < end) {
pavone@957 272 //terminate the list
pavone@958 273 *(cur++) = 0;
pavone@957 274 }
pavone@957 275 }
pavone@957 276 } else {
pavone@957 277 *(cur++) = 0;
pavone@957 278 *(cur++) = 0;
pavone@957 279 }
pavone@957 280 copy_to_guest(m68k, dst, buffer, cur-buffer);
pavone@957 281 break;
pavone@961 282 case 5:
pavone@961 283 //save state
pavone@961 284 if (gen->next_context) {
pavone@961 285 gen->next_context->save_state = dst + 1;
pavone@961 286 }
pavone@961 287 m68k->should_return = 1;
pavone@961 288 break;
pavone@961 289 case 6:
pavone@961 290 //load state
pavone@961 291 if (gen->next_context && gen->next_context->save_dir) {
pavone@961 292 char numslotname[] = "slot_0.gst";
pavone@961 293 char *slotname;
pavone@961 294 if (dst == QUICK_SAVE_SLOT) {
pavone@961 295 slotname = "quicksave.gst";
pavone@961 296 } else {
pavone@961 297 numslotname[5] = '0' + dst;
pavone@961 298 slotname = numslotname;
pavone@961 299 }
pavone@961 300 char const *parts[] = {gen->next_context->save_dir, "/", slotname};
pavone@961 301 char *gstpath = alloc_concat_m(3, parts);
pavone@961 302 uint32_t pc = load_gst(gen->next_context, gstpath);
pavone@961 303 free(gstpath);
pavone@961 304 gen->next_context->m68k->resume_pc = get_native_address_trans(gen->next_context->m68k, pc);
pavone@961 305 }
pavone@961 306 m68k->should_return = 1;
pavone@961 307 break;
pavone@873 308 }
pavone@866 309 default:
pavone@866 310 fprintf(stderr, "WARNING: write to undefined menu port %X\n", address);
pavone@866 311 }
pavone@866 312 menu->state = 0;
pavone@866 313 } else {
pavone@866 314 menu->latch = value;
pavone@866 315 menu->state = 1;
pavone@866 316 }
pavone@954 317 if (m68k->should_return) {
pavone@954 318 m68k->target_cycle = m68k->current_cycle;
pavone@954 319 }
pavone@866 320
pavone@866 321 return context;
pavone@866 322 }