mirror of
https://github.com/gwenhael-le-moine/x49gp.git
synced 2024-12-25 21:58:49 +01:00
648 lines
13 KiB
C
648 lines
13 KiB
C
/* $Id: main.c,v 1.30 2008/12/11 12:18:17 ecd Exp $
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <glib.h>
|
|
|
|
#include <x49gp.h>
|
|
#include <x49gp_ui.h>
|
|
#include <memory.h>
|
|
#include <s3c2410.h>
|
|
#include <s3c2410_power.h>
|
|
#include <s3c2410_timer.h>
|
|
#include <x49gp_timer.h>
|
|
|
|
#include "gdbstub.h"
|
|
|
|
static x49gp_t *x49gp;
|
|
|
|
/* LD TEMPO HACK */
|
|
CPUState *__GLOBAL_env;
|
|
|
|
int semihosting_enabled = 1;
|
|
|
|
uint8_t *phys_ram_base;
|
|
int phys_ram_size;
|
|
ram_addr_t ram_size = 0x80000; // LD ???
|
|
|
|
/* vl.c */
|
|
int singlestep;
|
|
|
|
#if !(defined(__APPLE__) || defined(_POSIX_C_SOURCE) && !defined(__sun__))
|
|
static void *oom_check(void *ptr)
|
|
{
|
|
if (ptr == NULL) {
|
|
abort();
|
|
}
|
|
return ptr;
|
|
}
|
|
#endif
|
|
|
|
void *qemu_memalign(size_t alignment, size_t size)
|
|
{
|
|
#if defined(__APPLE__) || defined(_POSIX_C_SOURCE) && !defined(__sun__)
|
|
int ret;
|
|
void *ptr;
|
|
ret = posix_memalign(&ptr, alignment, size);
|
|
if (ret != 0)
|
|
abort();
|
|
return ptr;
|
|
#elif defined(CONFIG_BSD)
|
|
return oom_check(valloc(size));
|
|
#else
|
|
return oom_check(memalign(alignment, size));
|
|
#endif
|
|
}
|
|
|
|
|
|
void qemu_init_vcpu(void *_env)
|
|
{
|
|
CPUState *env = _env;
|
|
|
|
env->nr_cores = 1;
|
|
env->nr_threads = 1;
|
|
}
|
|
|
|
int qemu_cpu_self(void *env)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
void qemu_cpu_kick(void *env)
|
|
{
|
|
}
|
|
|
|
void armv7m_nvic_set_pending(void *opaque, int irq)
|
|
{
|
|
abort();
|
|
}
|
|
int armv7m_nvic_acknowledge_irq(void *opaque)
|
|
{
|
|
abort();
|
|
}
|
|
void armv7m_nvic_complete_irq(void *opaque, int irq)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
void *
|
|
qemu_malloc(size_t size)
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
void *
|
|
qemu_mallocz(size_t size)
|
|
{
|
|
void *ptr;
|
|
|
|
ptr = qemu_malloc(size);
|
|
if (NULL == ptr)
|
|
return NULL;
|
|
memset(ptr, 0, size);
|
|
return ptr;
|
|
}
|
|
|
|
void
|
|
qemu_free(void *ptr)
|
|
{
|
|
free(ptr);
|
|
}
|
|
|
|
void *
|
|
qemu_vmalloc(size_t size)
|
|
{
|
|
#if defined(__linux__)
|
|
void *mem;
|
|
if (0 == posix_memalign(&mem, sysconf(_SC_PAGE_SIZE), size))
|
|
return mem;
|
|
return NULL;
|
|
#else
|
|
return valloc(size);
|
|
#endif
|
|
}
|
|
|
|
#define SWI_Breakpoint 0x180000
|
|
|
|
uint32_t
|
|
do_arm_semihosting(CPUState *env)
|
|
{
|
|
uint32_t number;
|
|
if (env->thumb) {
|
|
number = lduw_code(env->regs[15] - 2) & 0xff;
|
|
} else {
|
|
number = ldl_code(env->regs[15] - 4) & 0xffffff;
|
|
}
|
|
switch (number) {
|
|
case SWI_Breakpoint:
|
|
break;
|
|
|
|
case 0:
|
|
#ifdef DEBUG_X49GP_SYSCALL
|
|
printf("%s: SWI LR %08x: syscall %u: args %08x %08x %08x %08x %08x %08x %08x\n",
|
|
__FUNCTION__, env->regs[14], env->regs[0],
|
|
env->regs[1], env->regs[2], env->regs[3],
|
|
env->regs[4], env->regs[5], env->regs[6],
|
|
env->regs[7]);
|
|
#endif
|
|
|
|
#if 1
|
|
switch (env->regs[0]) {
|
|
case 305: /* Beep */
|
|
printf("%s: BEEP: frequency %u, time %u, override %u\n",
|
|
__FUNCTION__, env->regs[1], env->regs[2], env->regs[3]);
|
|
|
|
gdk_beep();
|
|
env->regs[0] = 0;
|
|
return 1;
|
|
|
|
case 28: /* CheckBeepEnd */
|
|
env->regs[0] = 0;
|
|
return 1;
|
|
|
|
case 29: /* StopBeep */
|
|
env->regs[0] = 0;
|
|
return 1;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
x49gp_set_idle(x49gp_t *x49gp, x49gp_arm_idle_t idle)
|
|
{
|
|
#ifdef DEBUG_X49GP_ARM_IDLE
|
|
if (idle != x49gp->arm_idle) {
|
|
printf("%s: arm_idle %u, idle %u\n", __FUNCTION__, x49gp->arm_idle, idle);
|
|
}
|
|
#endif
|
|
|
|
x49gp->arm_idle = idle;
|
|
|
|
if (x49gp->arm_idle == X49GP_ARM_RUN) {
|
|
x49gp->env->halted = 0;
|
|
} else {
|
|
x49gp->env->halted = 1;
|
|
cpu_exit(x49gp->env);
|
|
}
|
|
}
|
|
|
|
static void
|
|
arm_sighnd(int sig)
|
|
{
|
|
switch (sig) {
|
|
case SIGUSR1:
|
|
// stop_simulator = 1;
|
|
// x49gp->arm->CallDebug ^= 1;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: sig %u\n", __FUNCTION__, sig);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
x49gp_gtk_timer(void *data)
|
|
{
|
|
while (gtk_events_pending()) {
|
|
// printf("%s: gtk_main_iteration_do()\n", __FUNCTION__);
|
|
gtk_main_iteration_do(FALSE);
|
|
}
|
|
|
|
x49gp_mod_timer(x49gp->gtk_timer,
|
|
x49gp_get_clock() + X49GP_GTK_REFRESH_INTERVAL);
|
|
}
|
|
|
|
void
|
|
x49gp_lcd_timer(void *data)
|
|
{
|
|
x49gp_t *x49gp = data;
|
|
int64_t now, expires;
|
|
|
|
// printf("%s: lcd_update\n", __FUNCTION__);
|
|
x49gp_lcd_update(x49gp);
|
|
gdk_flush();
|
|
|
|
now = x49gp_get_clock();
|
|
expires = now + X49GP_LCD_REFRESH_INTERVAL;
|
|
|
|
// printf("%s: now: %lld, next update: %lld\n", __FUNCTION__, now, expires);
|
|
x49gp_mod_timer(x49gp->lcd_timer, expires);
|
|
}
|
|
|
|
struct options {
|
|
char *config;
|
|
int debug_port;
|
|
int start_debugger;
|
|
|
|
int more_options;
|
|
};
|
|
|
|
struct option_def;
|
|
|
|
typedef int (*option_action)(struct options *opt, struct option_def *match,
|
|
char *this_opt, char *param, char *progname);
|
|
|
|
struct option_def {
|
|
option_action action;
|
|
char *longname;
|
|
char shortname;
|
|
};
|
|
|
|
static int action_help(struct options *opt, struct option_def *match,
|
|
char *this_opt, char *param, char *progname);
|
|
static int action_debuglater(struct options *opt, struct option_def *match,
|
|
char *this_opt, char *param, char *progname);
|
|
static int action_debug(struct options *opt, struct option_def *match,
|
|
char *this_opt, char *param, char *progname);
|
|
|
|
static int action_unknown_with_param(struct options *opt,
|
|
struct option_def *match, char *this_opt,
|
|
char *param, char *progname);
|
|
static int action_longopt(struct options *opt, struct option_def *match,
|
|
char *this_opt, char *param, char *progname);
|
|
static int action_endopt(struct options *opt, struct option_def *match,
|
|
char *this_opt, char *param, char *progname);
|
|
|
|
struct option_def option_defs[] = {
|
|
{ action_help, "help", 'h' },
|
|
{ action_debuglater, "enable-debug", 'D' },
|
|
{ action_debug, "debug", 'd' },
|
|
|
|
{ action_longopt, NULL, '-' },
|
|
{ action_unknown_with_param, NULL, '=' },
|
|
{ action_endopt, "", '\0' }
|
|
};
|
|
|
|
static void
|
|
warn_unneeded_param(struct option_def *match, char *this_opt)
|
|
{
|
|
if (this_opt[1] == '-') {
|
|
fprintf(stderr, "The option \"--%s\" does not support"
|
|
" parameters\n", match->longname);
|
|
} else
|
|
fprintf(stderr, "The option '-%c' does not support parameters\n",
|
|
match->shortname);
|
|
}
|
|
|
|
static int
|
|
action_help(struct options *opt, struct option_def *match, char *this_opt,
|
|
char *param, char *progname)
|
|
{
|
|
if (param != NULL)
|
|
warn_unneeded_param(match, this_opt);
|
|
|
|
fprintf(stderr, "Emulator for HP 49G+ / 50G calculators\n"
|
|
"Usage: %s [<options>] [<config-file>]\n"
|
|
"Valid options:\n"
|
|
" -D, --enable-debug[=<port] enable the debugger interface\n"
|
|
" (default port: %u)\n"
|
|
" -d, --debug[=<port>] like -D, but also start the"
|
|
" debugger immediately\n"
|
|
" -h, --help print this message and exit\n"
|
|
"The config file is formatted as INI file and contains the"
|
|
" settings for which\n"
|
|
"persistence makes sense, like calculator model, CPU"
|
|
" registers, etc.\n"
|
|
"If the config file is omitted, ~/.%s/config is used.\n"
|
|
"Please consult the manual for more details on config file"
|
|
" settings.\n", progname, DEFAULT_GDBSTUB_PORT, progname);
|
|
exit(0);
|
|
}
|
|
|
|
static int
|
|
action_debuglater(struct options *opt, struct option_def *match, char *this_opt,
|
|
char *param, char *progname)
|
|
{
|
|
char *end;
|
|
int port;
|
|
|
|
if (param == NULL) {
|
|
if (opt->debug_port == 0)
|
|
opt->debug_port = DEFAULT_GDBSTUB_PORT;
|
|
return FALSE;
|
|
}
|
|
|
|
port = strtoul(param, &end, 0);
|
|
if ((end == param) || (*end != '\0')) {
|
|
fprintf(stderr, "Invalid port \"%s\", using default\n", param);
|
|
if (opt->debug_port == 0)
|
|
opt->debug_port = DEFAULT_GDBSTUB_PORT;
|
|
return TRUE;
|
|
}
|
|
|
|
if (opt->debug_port != 0 && opt->debug_port != DEFAULT_GDBSTUB_PORT)
|
|
fprintf(stderr, "Additional debug port \"%s\" specified,"
|
|
" overriding\n", param);
|
|
opt->debug_port = port;
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
action_debug(struct options *opt, struct option_def *match, char *this_opt,
|
|
char *param, char *progname)
|
|
{
|
|
opt->start_debugger = TRUE;
|
|
return action_debuglater(opt, match, this_opt, param, progname);
|
|
}
|
|
|
|
static int
|
|
action_longopt(struct options *opt, struct option_def *match, char *this_opt,
|
|
char *param, char *progname)
|
|
{
|
|
int i;
|
|
char *test_str, *option_str;
|
|
|
|
if (this_opt[1] != '-' || param != NULL) {
|
|
fprintf(stderr, "Unrecognized option '-', ignoring\n");
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < sizeof(option_defs) / sizeof(option_defs[0]); i++) {
|
|
if (option_defs[i].longname == NULL)
|
|
continue;
|
|
|
|
test_str = option_defs[i].longname;
|
|
option_str = this_opt + 2;
|
|
|
|
while (*test_str != '\0' && *test_str == *option_str) {
|
|
test_str++;
|
|
option_str++;
|
|
}
|
|
|
|
if (*test_str != '\0') continue;
|
|
|
|
switch (*option_str) {
|
|
case '\0':
|
|
(option_defs[i].action)(opt, option_defs + i, this_opt,
|
|
NULL, progname);
|
|
return TRUE;
|
|
case '=':
|
|
(option_defs[i].action)(opt, option_defs + i, this_opt,
|
|
option_str+2, progname);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "Unrecognized option \"%s\", ignoring\n", this_opt + 2);
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
action_unknown_with_param(struct options *opt, struct option_def *match,
|
|
char *this_opt, char *param, char *progname)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
action_endopt(struct options *opt, struct option_def *match, char *this_opt,
|
|
char *param, char *progname)
|
|
{
|
|
opt->more_options = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
parse_shortopt(struct options *opt, char *this_opt, char *progname)
|
|
{
|
|
char *option = this_opt + 1;
|
|
char *param;
|
|
int i;
|
|
|
|
if (*option == '\0') {
|
|
fprintf(stderr,
|
|
"Empty option present, ignoring\n");
|
|
return;
|
|
}
|
|
|
|
do {
|
|
for (i = 0; i < sizeof(option_defs) / sizeof(option_defs[0]);
|
|
i++) {
|
|
|
|
if (*option == option_defs[i].shortname) {
|
|
if (*(option + 1) == '=') {
|
|
param = option + 2;
|
|
} else {
|
|
param = NULL;
|
|
}
|
|
|
|
if ((option_defs[i].action)(opt, option_defs + i,
|
|
this_opt, param,
|
|
progname))
|
|
return;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (i == sizeof(option_defs) / sizeof(option_defs[0]))
|
|
fprintf(stderr,
|
|
"Unrecognized option '%c', ignoring\n",
|
|
*option);
|
|
option++;
|
|
} while (*option != '\0');
|
|
}
|
|
|
|
static void
|
|
parse_options(struct options *opt, int argc, char **argv, char *progname)
|
|
{
|
|
opt->more_options = TRUE;
|
|
|
|
while (argc > 1) {
|
|
switch (argv[1][0]) {
|
|
case '\0':
|
|
break;
|
|
break;
|
|
|
|
case '-':
|
|
if (opt->more_options) {
|
|
parse_shortopt(opt, argv[1], progname);
|
|
break;
|
|
}
|
|
/* FALL THROUGH */
|
|
|
|
default:
|
|
if (opt->config != NULL) {
|
|
fprintf(stderr,
|
|
"Additional config file \"%s\""
|
|
" specified, overriding\n",
|
|
argv[1]);
|
|
}
|
|
opt->config = argv[1];
|
|
}
|
|
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
ui_sighnd(int sig)
|
|
{
|
|
switch (sig) {
|
|
case SIGINT:
|
|
case SIGQUIT:
|
|
case SIGTERM:
|
|
x49gp->arm_exit = 1;
|
|
cpu_exit(x49gp->env);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
char *progname, *progpath;
|
|
int error;
|
|
struct options opt;
|
|
const char *home;
|
|
|
|
|
|
progname = g_path_get_basename(argv[0]);
|
|
progpath = g_path_get_dirname(argv[0]);
|
|
|
|
|
|
gtk_init(&argc, &argv);
|
|
|
|
|
|
opt.config = NULL;
|
|
opt.debug_port = 0;
|
|
opt.start_debugger = FALSE;
|
|
parse_options(&opt, argc, argv, progname);
|
|
|
|
x49gp = malloc(sizeof(x49gp_t));
|
|
if (NULL == x49gp) {
|
|
fprintf(stderr, "%s: %s:%u: Out of memory\n",
|
|
progname, __FUNCTION__, __LINE__);
|
|
exit(1);
|
|
}
|
|
memset(x49gp, 0, sizeof(x49gp_t));
|
|
|
|
#ifdef DEBUG_X49GP_MAIN
|
|
fprintf(stderr, "_SC_PAGE_SIZE: %08lx\n", sysconf(_SC_PAGE_SIZE));
|
|
|
|
printf("%s:%u: x49gp: %p\n", __FUNCTION__, __LINE__, x49gp);
|
|
#endif
|
|
|
|
INIT_LIST_HEAD(&x49gp->modules);
|
|
|
|
|
|
x49gp->progname = progname;
|
|
x49gp->progpath = progpath;
|
|
x49gp->clk_tck = sysconf(_SC_CLK_TCK);
|
|
|
|
x49gp->emulator_fclk = 75000000;
|
|
x49gp->PCLK_ratio = 4;
|
|
x49gp->PCLK = 75000000 / 4;
|
|
|
|
//cpu_set_log(0xffffffff);
|
|
cpu_exec_init_all(0);
|
|
x49gp->env = cpu_init("arm926");
|
|
__GLOBAL_env = x49gp->env;
|
|
|
|
// cpu_set_log(cpu_str_to_log_mask("all"));
|
|
|
|
x49gp_timer_init(x49gp);
|
|
|
|
x49gp->gtk_timer = x49gp_new_timer(X49GP_TIMER_REALTIME,
|
|
x49gp_gtk_timer, x49gp);
|
|
x49gp->lcd_timer = x49gp_new_timer(X49GP_TIMER_VIRTUAL,
|
|
x49gp_lcd_timer, x49gp);
|
|
|
|
x49gp_ui_init(x49gp);
|
|
|
|
x49gp_s3c2410_arm_init(x49gp);
|
|
|
|
x49gp_flash_init(x49gp);
|
|
x49gp_sram_init(x49gp);
|
|
|
|
x49gp_s3c2410_init(x49gp);
|
|
|
|
if (x49gp_modules_init(x49gp)) {
|
|
exit(1);
|
|
}
|
|
|
|
if (opt.config == NULL) {
|
|
char config_dir[strlen(progname) + 2];
|
|
|
|
home = g_get_home_dir();
|
|
sprintf(config_dir, ".%s", progname);
|
|
opt.config = g_build_filename(home, config_dir,
|
|
"config", NULL);
|
|
}
|
|
|
|
x49gp->basename = g_path_get_dirname(opt.config);
|
|
x49gp->debug_port = opt.debug_port;
|
|
|
|
error = x49gp_modules_load(x49gp, opt.config);
|
|
if (error) {
|
|
if (error != -EAGAIN) {
|
|
exit(1);
|
|
}
|
|
x49gp_modules_reset(x49gp, X49GP_RESET_POWER_ON);
|
|
}
|
|
// x49gp_modules_reset(x49gp, X49GP_RESET_POWER_ON);
|
|
|
|
signal(SIGINT, ui_sighnd);
|
|
signal(SIGTERM, ui_sighnd);
|
|
signal(SIGQUIT, ui_sighnd);
|
|
|
|
signal(SIGUSR1, arm_sighnd);
|
|
|
|
|
|
x49gp_set_idle(x49gp, 0);
|
|
|
|
// stl_phys(0x08000a1c, 0x55555555);
|
|
|
|
|
|
x49gp_mod_timer(x49gp->gtk_timer, x49gp_get_clock());
|
|
x49gp_mod_timer(x49gp->lcd_timer, x49gp_get_clock());
|
|
|
|
|
|
if(opt.debug_port != 0 && opt.start_debugger) {
|
|
gdbserver_start(opt.debug_port);
|
|
gdb_handlesig(x49gp->env, 0);
|
|
}
|
|
|
|
x49gp_main_loop(x49gp);
|
|
|
|
|
|
x49gp_modules_save(x49gp, opt.config);
|
|
x49gp_modules_exit(x49gp);
|
|
|
|
|
|
#if 0
|
|
printf("ClkTicks: %lu\n", ARMul_Time(x49gp->arm));
|
|
printf("D TLB: hit0 %lu, hit1 %lu, search %lu (%lu), walk %lu\n",
|
|
x49gp->mmu->dTLB.hit0, x49gp->mmu->dTLB.hit1,
|
|
x49gp->mmu->dTLB.search, x49gp->mmu->dTLB.nsearch,
|
|
x49gp->mmu->dTLB.walk);
|
|
printf("I TLB: hit0 %lu, hit1 %lu, search %lu (%lu), walk %lu\n",
|
|
x49gp->mmu->iTLB.hit0, x49gp->mmu->iTLB.hit1,
|
|
x49gp->mmu->iTLB.search, x49gp->mmu->iTLB.nsearch,
|
|
x49gp->mmu->iTLB.walk);
|
|
#endif
|
|
return 0;
|
|
}
|